Index: /branches/pap/DataStore/lib/DataStore/FileSet/Parser.pm
===================================================================
--- /branches/pap/DataStore/lib/DataStore/FileSet/Parser.pm	(revision 28483)
+++ /branches/pap/DataStore/lib/DataStore/FileSet/Parser.pm	(revision 28484)
@@ -100,5 +100,5 @@
 
     $p{base_uri} =~ qr|(.*?/)index.txt|;
-    $self->{base_uri} = $1; 
+    $self->{base_uri} = $1;
 
     return $self;
@@ -129,5 +129,4 @@
         {
             type    => SCALAR,
-            regex   => qr/\S+/, # string with at least 1 non WS char
         }
     );
@@ -139,5 +138,5 @@
         next LINE if $line =~ /^\s*$/;
 
-        # comment lines   
+        # comment lines
         next LINE if $line =~ /^\s*\#/;
 
@@ -183,5 +182,5 @@
             carp "line $lineno: type $type unknown: $line";
             next LINE;
-        } 
+        }
 
         my @extra = @fields[3 .. $#fields] if $#fields >= 3;
@@ -192,5 +191,5 @@
             datetime    => $datetime,
             type        => $type,
-            extra       => \@extra, 
+            extra       => \@extra,
             uri         => $self->base_uri . $fileset . '/index.txt',
         });
Index: /branches/pap/DataStore/lib/DataStore/Product/Parser.pm
===================================================================
--- /branches/pap/DataStore/lib/DataStore/Product/Parser.pm	(revision 28483)
+++ /branches/pap/DataStore/lib/DataStore/Product/Parser.pm	(revision 28484)
@@ -132,5 +132,4 @@
         {
             type    => SCALAR,
-            regex   => qr/\S+/, # string with at least 1 non WS char
         }
     );
@@ -142,5 +141,5 @@
         next LINE if $line =~ /^\s*$/;
 
-        # comment lines   
+        # comment lines
         next LINE if $line =~ /^\s*\#/;
 
@@ -159,5 +158,5 @@
                 next LINE;
             }
-        
+
             # strip leading and trailing whitespace
             $field =~ s/^\s+//;
@@ -184,5 +183,5 @@
             carp "line $lineno: type $type unknown: $line";
             next LINE;
-        } 
+        }
 
         # fifo
Index: /branches/pap/DataStore/lib/DataStore/Utils.pm
===================================================================
--- /branches/pap/DataStore/lib/DataStore/Utils.pm	(revision 28483)
+++ /branches/pap/DataStore/lib/DataStore/Utils.pm	(revision 28484)
@@ -44,5 +44,5 @@
 %KNOWN_FILE_TYPES = map { $_ => 1 } qw( chip psrequest psresults pstamp chipproc warp stack diff ipp-mops table text xml tgz fits );
 %KNOWN_FILESET_TYPES = map { $_ => 1 } qw( OBJECT BIAS DARK SKYFLAT DOMEFLAT OOF SHACKHARTMANN PSREQUEST PSRESULTS IPP-MOPS XRAY FOCUS MOPS_DETECTABILITY_QUERY MOPS_DETECTABILITY_RESPONSE MOPS_TRANSIENT_DETECTIONS LED notset IPP_PSPS IPP-DIST);
-%KNOWN_PRODUCT_TYPES = map { $_ => 1 } qw( image dump psrequest psresults table ipp-dist ipp-misc);
+%KNOWN_PRODUCT_TYPES = map { $_ => 1 } qw( image dump psrequest psresults table ipp-dist ipp-misc dqresults);
 
 =pod
Index: /branches/pap/Nebulous-Server/bin/nebdiskd
===================================================================
--- /branches/pap/Nebulous-Server/bin/nebdiskd	(revision 28483)
+++ /branches/pap/Nebulous-Server/bin/nebdiskd	(revision 28484)
@@ -224,4 +224,13 @@
                 }
             };
+
+            # fetch stats on the mounted device.  this has to be done AFTER
+            # we determine if it's a valid mountpoint incase
+            # is_mountpoint() invokes the automounter
+            my $dev_info = df($mountpoint, 1024);
+            unless (defined $dev_info) {
+		$valid_mountpoint = 0;
+            }
+
             if (!$valid_mountpoint) {
                 # try is_mountpoint() again if $retry > 1
@@ -254,5 +263,5 @@
             # we determine if it's a valid mountpoint incase
             # is_mountpoint() invokes the automounter
-            my $dev_info = df($mountpoint, 1024);
+            $dev_info = df($mountpoint, 1024);
             unless (defined $dev_info) {
                 $log->error("can't find device info for $mountpoint");
Index: /branches/pap/Nebulous-Server/lib/Nebulous/Server.pm
===================================================================
--- /branches/pap/Nebulous-Server/lib/Nebulous/Server.pm	(revision 28483)
+++ /branches/pap/Nebulous-Server/lib/Nebulous/Server.pm	(revision 28484)
@@ -1598,4 +1598,58 @@
 }
 
+sub find_ext_id_by_volume
+{
+    my $self = shift;
+    my $log = $self->log;
+    $log->debug("entered - @_");
+    my ($vol_name,$limit) = validate_pos(@_,
+					{
+					    type      => SCALAR,
+# # 					    callbacks => {
+# # 						'is valid volume name' => sub {
+# # 						    return 1 if not defined $_[0];
+# # 						    $self->_is_valid_volume_name($_[0])
+# # 						},
+# 					    },
+					},
+					{
+					    type      => SCALAR|UNDEF,
+					    optional => 1,
+					},
+	);
+    unless (defined($limit)) {
+	$limit = 50000;
+    }
+
+    my $sql = $self->sql;
+    my @ext_ids;
+    my $db = $self->_db_for_index(0);
+    eval {
+	my $query;
+	$query = $db->prepare_cached( $sql->get_ext_id_by_vol_name );
+	my $rows = $query->execute($vol_name, 1,$limit);
+	unless ($rows > 0) {
+	    $query->finish;
+	    $log->logdie("no instances on storage volume or volume is not avaiable for volume: $vol_name");
+	}
+        while (my $row = $query->fetchrow_hashref) {
+            my $ext_id = $row->{ 'ext_id' };
+            push @ext_ids, $ext_id if $ext_id;
+        }
+    };
+    if ($@) {
+        $db->rollback;
+        $log->logdie("database error: $@");
+    }
+
+    # XXX remove this?
+    $log->logdie("no ext_ids found") unless (scalar @ext_ids);
+
+    $log->debug("found: \@ext_ids");
+
+    $log->debug("leaving");
+
+    return \@ext_ids;
+}
 
 sub find_instances
Index: /branches/pap/Nebulous-Server/lib/Nebulous/Server/SQL.pm
===================================================================
--- /branches/pap/Nebulous-Server/lib/Nebulous/Server/SQL.pm	(revision 28483)
+++ /branches/pap/Nebulous-Server/lib/Nebulous/Server/SQL.pm	(revision 28484)
@@ -266,4 +266,18 @@
             AND mountedvol.name = ?
             AND mountedvol.available = ?
+    },
+    get_ext_id_by_vol_name => qq{
+        SELECT
+            ext_id
+        FROM instance
+        JOIN storage_object
+            USING (so_id)
+        JOIN mountedvol
+            USING(vol_id)
+        JOIN volume
+            USING(vol_id)
+        WHERE volume.name = ?
+            AND mountedvol.available = ?
+        LIMIT ?
     },
     # volume handler
Index: /branches/pap/Nebulous/Build.PL
===================================================================
--- /branches/pap/Nebulous/Build.PL	(revision 28483)
+++ /branches/pap/Nebulous/Build.PL	(revision 28484)
@@ -113,7 +113,9 @@
         bin/neb-locate
         bin/neb-ls
+        bin/neb-migrate
         bin/neb-mv
         bin/neb-replicate
         bin/neb-rm
+        bin/neb-shift
         bin/neb-stat
         bin/neb-swap
Index: /branches/pap/Nebulous/MANIFEST
===================================================================
--- /branches/pap/Nebulous/MANIFEST	(revision 28483)
+++ /branches/pap/Nebulous/MANIFEST	(revision 28484)
@@ -14,7 +14,9 @@
 bin/neb-insert
 bin/neb-ls
+bin/neb-migrate
 bin/neb-mv
 bin/neb-replicate
 bin/neb-rm
+bin/neb-shift
 bin/neb-stat
 bin/neb-swap
Index: /branches/pap/Nebulous/bin/neb-migrate
===================================================================
--- /branches/pap/Nebulous/bin/neb-migrate	(revision 28484)
+++ /branches/pap/Nebulous/bin/neb-migrate	(revision 28484)
@@ -0,0 +1,180 @@
+#!/usr/bin/env perl
+
+# Copyright (C) 2007-2010  Joshua Hoblitt/Chris Waters
+
+use strict;
+use warnings FATAL => qw( all );
+
+use vars qw( $VERSION );
+$VERSION = '0.03';
+
+use Nebulous::Client;
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version );
+use Pod::Usage qw( pod2usage );
+
+my (
+    $server,
+    $limit,
+    $dest_volume,
+    $no_pretend,
+#    $recursive,
+);
+
+$server = $ENV{'NEB_SERVER'} unless $server;
+
+GetOptions(
+    'server|s=s'    => \$server,
+    'limit|l=s'     => \$limit,
+    'destination|d=s' => \$dest_volume,
+    'no_pretend'    => \$no_pretend,
+) || pod2usage( 2 );
+
+if ($limit && $limit <= 0) {
+    die "Limit must be positive.";
+}
+
+
+my $host = shift;
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage( -msg => "Required options: --server", -exitval => 2 )
+    unless $server;
+
+my $neb = Nebulous::Client->new(
+    proxy => "$server",
+);
+
+die "can't connected to Nebulous Server: $server"
+    unless defined $neb;
+
+# Find the keys on this volume.
+my $keys;
+if ($limit) {
+    $keys = $neb->find_ext_id_by_volume($host,$limit);
+}
+else {
+    $keys = $neb->find_ext_id_by_volume($host);
+}
+
+# Print the keys on this volume, and if we're not pretending, do the neb-shift code to move them off it.
+if ($keys) {
+    foreach my $src (@{ $keys }) {
+	print "$src";
+	if ($no_pretend) {
+	    print "\t";
+	    my $uris = $neb->find_instances($src, "~$host");
+	    unless (defined $uris) {
+		die "No copy of $src exists on $host";
+	    }
+	    
+	    my $status;
+	    if ($dest_volume) {
+		$status = $neb->replicate( $src, $dest_volume);
+	    }
+	    else {
+		$status = $neb->replicate($src);
+	    }
+	    die "Replicate phase failed for $src ($host) ($dest_volume)" unless $status;
+	    
+	    $status = $neb->cull($src,$host);
+	    
+	    warn "Cull phase failed for $src from $host" unless $status;
+	    
+	    print "Success!";
+	}
+	print "\n";
+    }
+}
+
+__END__
+
+=pod
+
+=head1 NAME
+
+neb-migrate - list Nebulous keys on a specific volume and move them off it
+
+=head1 SYNOPSIS
+
+    neb-migrate [--server <URL>] [--limit <N>] [--no_pretend] <volume>
+
+=head1 DESCRIPTION
+
+This program list Nebulous keys found on volume <<volume>>, and will move them 
+to another volume if requested to do so.  
+
+=head1 OPTIONS
+
+=over 4
+
+Optional
+
+=item * --limit
+
+Limit the number of results.
+
+=item * --no_pretend
+
+Actually move the files off of the specified host.
+
+=item * --destination|-d <dest_volume>
+
+Specify the destination volume for the keys moved.
+
+=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 SUPPORT
+
+Please contact the author directly via e-mail.
+
+=head1 AUTHOR
+
+Chris Waters <watersc1@ifa.hawaii.edu>
+
+=head1 COPYRIGHT
+
+Copyright (C) 2007-2010 Josh Hoblitt / Chris Waters.  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-addvol>, L<nebdiskd>, L<neb-df>,
+L<neb-touch>, C<regex(7)>
+
+=cut
Index: /branches/pap/Nebulous/bin/neb-shift
===================================================================
--- /branches/pap/Nebulous/bin/neb-shift	(revision 28484)
+++ /branches/pap/Nebulous/bin/neb-shift	(revision 28484)
@@ -0,0 +1,146 @@
+#!/usr/bin/env perl
+
+# Copyright (C) 2007-2008  Joshua Hoblitt
+#
+# $Id: neb-shift,v 1.5 2008-01-22 21:40:39 jhoblitt Exp $
+
+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 );
+
+my ($volume, $server);
+
+$server = $ENV{'NEB_SERVER'} unless $server;
+
+GetOptions(
+    'volume=s'      => \$volume,
+    'server|s=s'    => \$server,
+) || pod2usage( 2 );
+
+my $src = shift;
+my $abandon_vol = shift;
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage( -msg => "Required options: --server <source key> <source volume>",
+        -exitval => 2 )
+        unless defined $server and defined $src and defined $abandon_vol;
+
+my $neb = Nebulous::Client->new(
+    proxy => "$server",
+);
+
+die "can't connected to Nebulous Server: $server"
+    unless defined $neb;
+
+my $uris = $neb->find_instances($src, "~$abandon_vol");
+unless (defined $uris) {
+    die "No copy of $src exists on $abandon_vol";
+}
+
+my $status;
+if ($volume) {
+    $status = $neb->replicate( $src, $volume);
+}
+else {
+    $status = $neb->replicate($src);
+}
+die "Replicate phase failed for $src ($abandon_vol) ($volume)" unless $status;
+
+$status = $neb->cull($src,$abandon_vol);
+
+warn "Cull phase failed for $src from $abandon_vol" unless $status;
+
+__END__
+
+=pod
+
+=head1 NAME
+
+neb-shift - list Nebulous keys
+
+=head1 SYNOPSIS
+
+    neb-shift [--server <URL>] [--volume <destination volume name>] <source key> <source volume>
+
+=head1 DESCRIPTION
+
+Shift a Nebulous storage key from a specific source volume, optionally assigning it
+to a specific destination volume.
+
+=head1 OPTIONS
+
+=over 4
+
+=item * --server|-s <URL>
+
+URL of the Nebulous server to connect to.
+
+Optional if the appropriate environment variable is set.
+
+=item * --volume <volume name>
+
+The volume to place the key's new storage instance on.
+
+Optional.
+
+=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
+
+Just me, myself, and I.
+
+=head1 SUPPORT
+
+Please contact the author directly via e-mail.
+
+=head1 AUTHOR
+
+Joshua Hoblitt <jhoblitt@cpan.org>
+
+=head1 COPYRIGHT
+
+Copyright (C) 2007-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-addvol>, L<nebdiskd>, L<neb-df>,
+L<neb-touch>, C<regex(7)>
+
+=cut
Index: /branches/pap/Nebulous/lib/Nebulous/Client.pm
===================================================================
--- /branches/pap/Nebulous/lib/Nebulous/Client.pm	(revision 28483)
+++ /branches/pap/Nebulous/lib/Nebulous/Client.pm	(revision 28484)
@@ -790,4 +790,38 @@
 }
 
+sub find_ext_id_by_volume
+{
+    my $self = shift;
+    my ($vol_name, $limit) = validate_pos( @_,
+					   { 
+					       type => SCALAR,
+					   },
+					   {
+					       type => SCALAR|UNDEF,
+					       optional => 1,
+					   },
+	);
+    
+    $log->debug( "entered - @_" );
+
+    my $response = $self->{ 'server' }->find_ext_id_by_volume( $vol_name, $limit);
+    if ( $response->fault ) {
+	$self->set_err($response->faultstring);
+	if ($response->faultstring =~ /no instances on storage volume/) {
+	    $log->debug( "leaving" );
+	    return;
+	}
+
+	$log->logdie("unhandled fault - ", $self->err);
+    }
+
+    my $ext_ids = $response->result;
+    
+    $log->debug( "server found @$ext_ids" );
+    $log->debug( "leaving" );
+    
+    return($ext_ids);
+}
+
 sub find_instances_for_cull
 {
Index: /branches/pap/Ohana/Makefile.in
===================================================================
--- /branches/pap/Ohana/Makefile.in	(revision 28483)
+++ /branches/pap/Ohana/Makefile.in	(revision 28484)
@@ -31,4 +31,5 @@
 delstar     \
 dvosplit    \
+dvomerge    \
 elixir      \
 gastro      \
Index: /branches/pap/Ohana/configure.tcsh
===================================================================
--- /branches/pap/Ohana/configure.tcsh	(revision 28483)
+++ /branches/pap/Ohana/configure.tcsh	(revision 28484)
@@ -186,5 +186,9 @@
 
 # use_gnu99
-if ($use_gnu99) set CFLAGS = "$CFLAGS -std=gnu99"
+if ($use_gnu99) then
+    set CFLAGS = "$CFLAGS -std=gnu99"
+else
+    set CFLAGS = "$CFLAGS -std=gnu89"
+endif
 
 # no_largefiles
@@ -670,5 +674,5 @@
   --use-tcmalloc          use the alternate tcmalloc from Google
   --use-gnu99             use gnu99 flags to ensure C99 compatibility
-  --use-largefiles        ensure large file compatibility
+  --no-largefiles         skip large file compatibility
 
 Installation directories:
Index: /branches/pap/Ohana/src/addstar/src/ConfigInit.c
===================================================================
--- /branches/pap/Ohana/src/addstar/src/ConfigInit.c	(revision 28483)
+++ /branches/pap/Ohana/src/addstar/src/ConfigInit.c	(revision 28484)
@@ -7,5 +7,5 @@
   double ZERO_POINT;
   char *config, *file;
-  char RadiusWord[80];
+  char RadiusWord[80], tmpword[80];
   char CatdirPhotcodeFile[256];
   char MasterPhotcodeFile[256];
@@ -68,6 +68,29 @@
   ScanConfig (config, "CCDNUM-KEYWORD",         "%s",  0, CCDNumKeyword);
   ScanConfig (config, "ST-KEYWORD",             "%s",  0, STKeyword);
-  ScanConfig (config, "OBSERVATORY-LATITUDE",   "%lf", 0, &Latitude);
-  ScanConfig (config, "OBSERVATORY-LONGITUDE",  "%lf", 0, &Longitude);
+
+  ScanConfig (config, "OBSERVATORY-LATITUDE",   "%s",  0, tmpword);
+  if (!strcasecmp(tmpword, "NONE")) {
+      fprintf (stderr, "observatory latitude is not set\n");
+      Latitude = NAN;
+  } else {
+      ScanConfig (config, "OBSERVATORY-LATITUDE",   "%lf", 0, &Latitude);
+  }
+  ScanConfig (config, "OBSERVATORY-LONGITUDE",   "%s",  0, tmpword);
+  if (!strcasecmp(tmpword, "NONE")) {
+      fprintf (stderr, "observatory longitude is not set\n");
+      Longitude = NAN;
+  } else {
+      ScanConfig (config, "OBSERVATORY-LONGITUDE",  "%lf", 0, &Longitude);
+  }
+  fprintf (stderr, "observatory @ (%f,%f)\n", Longitude, Latitude);
+
+  if (!strcasecmp(STKeyword, "NONE")) {
+      if (isnan(Longitude)) { 
+	  fprintf (stderr, "WARNING: ST cannot be determined for this image (no ST Keyword, no longitude)\n");
+      } else {
+	  fprintf (stderr, "ST Keyword is not defined, ST will be derived from time & longitude\n");
+      }
+  }
+
   ScanConfig (config, "SUBPIX_DATAFILE",        "%s",  0, SubpixDatafile);
 
Index: /branches/pap/Ohana/src/addstar/src/FilterStars.c
===================================================================
--- /branches/pap/Ohana/src/addstar/src/FilterStars.c	(revision 28483)
+++ /branches/pap/Ohana/src/addstar/src/FilterStars.c	(revision 28484)
@@ -54,5 +54,5 @@
     /* calculate accurate per-star airmass and azimuth */
     stars[N].measure.airmass = airmass (image[0].secz, stars[N].average.R, stars[N].average.D, image[0].sidtime, image[0].latitude);
-    stars[N].measure.az      = azimuth (image[0].sidtime - stars[N].average.R, stars[N].average.D, image[0].latitude);
+    stars[N].measure.az      = azimuth (15.0*image[0].sidtime - stars[N].average.R, stars[N].average.D, image[0].latitude);
     stars[N].measure.Mcal    = image[0].Mcal;
     stars[N].measure.t       = image[0].tzero + 1e-4*stars[N].measure.Yccd*image[0].trate;  /* trate is in 0.1 msec / row */
Index: /branches/pap/Ohana/src/addstar/src/LoadStars.c
===================================================================
--- /branches/pap/Ohana/src/addstar/src/LoadStars.c	(revision 28483)
+++ /branches/pap/Ohana/src/addstar/src/LoadStars.c	(revision 28484)
@@ -81,5 +81,5 @@
       continue;
     }
-    if (VERBOSE) fprintf (stderr, "file %s has %d headers, including %lld images\n", file[i], Nheaders, (long long) NheaderSets);
+    if (VERBOSE) fprintf (stderr, "file %s has %d headers, including "OFF_T_FMT" images\n", file[i], Nheaders,  NheaderSets);
 
     /* supplied photcode is incompatible with multi-chip images */
Index: /branches/pap/Ohana/src/addstar/src/ReadImageHeader.c
===================================================================
--- /branches/pap/Ohana/src/addstar/src/ReadImageHeader.c	(revision 28483)
+++ /branches/pap/Ohana/src/addstar/src/ReadImageHeader.c	(revision 28484)
@@ -148,5 +148,5 @@
   image[0].fwhm_y = tmp * 25.0 * image[0].coords.cdelt1 * 3600.0;
 
-  if (STKeyword[0]) {
+  if (STKeyword[0] && strcasecmp(STKeyword, "NONE")) {
     /* get ST (used for airmass calculation) */
     gfits_scan (header, STKeyword, "%s", 1, line);
Index: /branches/pap/Ohana/src/addstar/src/SkyTableFromTychoIndex.c
===================================================================
--- /branches/pap/Ohana/src/addstar/src/SkyTableFromTychoIndex.c	(revision 28483)
+++ /branches/pap/Ohana/src/addstar/src/SkyTableFromTychoIndex.c	(revision 28484)
@@ -147,5 +147,5 @@
     skytable[0].filename[i] = NULL;
   }
-  if (VERBOSE) fprintf (stderr, "loaded %lld tables from tycho index\n", (long long) skytable[0].Nregions);
+  if (VERBOSE) fprintf (stderr, "loaded "OFF_T_FMT" tables from tycho index\n",  skytable[0].Nregions);
 
   return (skytable);
Index: /branches/pap/Ohana/src/addstar/src/UpdateImageIDs.c
===================================================================
--- /branches/pap/Ohana/src/addstar/src/UpdateImageIDs.c	(revision 28483)
+++ /branches/pap/Ohana/src/addstar/src/UpdateImageIDs.c	(revision 28484)
@@ -55,7 +55,7 @@
 
   if (isEmpty) {
-    dvo_image_addrows (&db, NULL, 0);
+    if (!dvo_image_addrows (&db, NULL, 0)) Shutdown ("failed to create image table");
     SetProtect (TRUE);
-    dvo_image_update (&db, VERBOSE);
+    if (!dvo_image_update (&db, VERBOSE)) Shutdown ("failed to update image table");
     SetProtect (FALSE);
   } else {
Index: /branches/pap/Ohana/src/addstar/src/addstar.c
===================================================================
--- /branches/pap/Ohana/src/addstar/src/addstar.c	(revision 28483)
+++ /branches/pap/Ohana/src/addstar/src/addstar.c	(revision 28484)
@@ -48,5 +48,5 @@
 	newlist = SkyListByImage (sky, -1, &images[i]);
 	SkyListMerge (&skylist, newlist);
-	if (VERBOSE) fprintf (stderr, "added %lld regions to yield %lld total\n", (long long) newlist[0].Nregions, (long long) skylist[0].Nregions);
+	if (VERBOSE) fprintf (stderr, "added "OFF_T_FMT" regions to yield "OFF_T_FMT" total\n",  newlist[0].Nregions,  skylist[0].Nregions);
 	SkyListFree (newlist);
       }
@@ -80,5 +80,5 @@
     skylist = tmp;
   }
-  if (VERBOSE) fprintf (stderr, "writing to %lld regions\n", (long long) skylist[0].Nregions);
+  if (VERBOSE) fprintf (stderr, "writing to "OFF_T_FMT" regions\n",  skylist[0].Nregions);
 
   /* don't load the object tables for only_images, unless we are getting the calibration. */
@@ -213,5 +213,5 @@
   gettimeofday (&stop, NULL);
   dtime = DTIME (stop, start);
-  fprintf (stderr, "SUCCESS: elapsed time %9.4f sec for %5d stars (%5d matches), %6lld average, %7lld measure\n", dtime, Nstars, Nmatch, (long long) Naverage, (long long) Nmeasure);
+  fprintf (stderr, "SUCCESS: elapsed time %9.4f sec for %5d stars (%5d matches), "OFF_T_FMT" average, "OFF_T_FMT" measure\n", dtime, Nstars, Nmatch,  Naverage,  Nmeasure);
 
   exit (0);
Index: /branches/pap/Ohana/src/addstar/src/find_matches.c
===================================================================
--- /branches/pap/Ohana/src/addstar/src/find_matches.c	(revision 28483)
+++ /branches/pap/Ohana/src/addstar/src/find_matches.c	(revision 28484)
@@ -3,5 +3,5 @@
 int find_matches (SkyRegion *region, Stars *stars, unsigned int NstarsIn, Catalog *catalog, AddstarClientOptions options) {
 
-  off_t i, j, n, N, J, status, Nstars;
+  off_t i, j, n, N, J, Nstars;
   double RADIUS, RADIUS2;
   double *X1, *Y1, *X2, *Y2;
@@ -9,5 +9,5 @@
   off_t *N1, *N2, *next_meas;
   off_t Nave, NAVE, Nmeas, NMEAS, Nmatch;
-  int Nsecfilt, Nsec;
+  int Nsecfilt, Nsec, status;
   unsigned int objID, catID;
   Coords tcoords;
@@ -356,5 +356,5 @@
   catalog[0].Nmeasure = Nmeas;
   catalog[0].Nsecf_mem = Nave*Nsecfilt;
-  if (VERBOSE) fprintf (stderr, "Nstars, Nave, Nmeas: %lld %lld %lld, (%lld matches)\n", (long long) Nstars, (long long) Nave, (long long) Nmeas, (long long) Nmatch);
+  if (VERBOSE) fprintf (stderr, "Nstars, Nave, Nmeas: "OFF_T_FMT" "OFF_T_FMT" "OFF_T_FMT", ("OFF_T_FMT" matches)\n",  Nstars,  Nave,  Nmeas,  Nmatch);
 
   free (catalog[0].found);
Index: /branches/pap/Ohana/src/addstar/src/find_matches_closest.c
===================================================================
--- /branches/pap/Ohana/src/addstar/src/find_matches_closest.c	(revision 28483)
+++ /branches/pap/Ohana/src/addstar/src/find_matches_closest.c	(revision 28484)
@@ -350,5 +350,5 @@
   catalog[0].Nmeasure = Nmeas;
   catalog[0].Nsecf_mem = Nave*Nsecfilt;
-  if (VERBOSE) fprintf (stderr, "Nstars, Nave, Nmeas: %lld %lld %lld, (%lld matches)\n", (long long) Nstars, (long long) Nave, (long long) Nmeas, (long long) Nmatch);
+  if (VERBOSE) fprintf (stderr, "Nstars, Nave, Nmeas: "OFF_T_FMT" "OFF_T_FMT" "OFF_T_FMT", ("OFF_T_FMT" matches)\n",  Nstars,  Nave,  Nmeas,  Nmatch);
 
   free (catalog[0].found);
Index: /branches/pap/Ohana/src/addstar/src/find_matches_closest_refstars.c
===================================================================
--- /branches/pap/Ohana/src/addstar/src/find_matches_closest_refstars.c	(revision 28483)
+++ /branches/pap/Ohana/src/addstar/src/find_matches_closest_refstars.c	(revision 28484)
@@ -361,5 +361,5 @@
   catalog[0].Nmeasure = Nmeas;
   catalog[0].Nsecf_mem = Nave*Nsecfilt;
-  if (VERBOSE) fprintf (stderr, "Nstars, Nave, Nmeas: %lld %lld %lld, (%lld matches)\n", (long long) Nstars, (long long) Nave, (long long) Nmeas, (long long) Nmatch);
+  if (VERBOSE) fprintf (stderr, "Nstars, Nave, Nmeas: "OFF_T_FMT" "OFF_T_FMT" "OFF_T_FMT", ("OFF_T_FMT" matches)\n",  Nstars,  Nave,  Nmeas,  Nmatch);
 
   free (catalog[0].found);
Index: /branches/pap/Ohana/src/addstar/src/find_matches_refstars.c
===================================================================
--- /branches/pap/Ohana/src/addstar/src/find_matches_refstars.c	(revision 28483)
+++ /branches/pap/Ohana/src/addstar/src/find_matches_refstars.c	(revision 28484)
@@ -329,5 +329,5 @@
   catalog[0].Nmeasure = Nmeas;
   catalog[0].Nsecf_mem = Nave*Nsecfilt;
-  if (VERBOSE) fprintf (stderr, "Nstars, Nave, Nmeas: %lld %lld %lld, (%lld matches)\n", (long long) Nstars, (long long) Nave, (long long) Nmeas, (long long) Nmatch);
+  if (VERBOSE) fprintf (stderr, "Nstars, Nave, Nmeas: %d "OFF_T_FMT" "OFF_T_FMT", ("OFF_T_FMT" matches)\n",  Nstars,  Nave,  Nmeas,  Nmatch);
 
   free (catalog[0].found);
Index: /branches/pap/Ohana/src/addstar/src/sky_tessalation.c
===================================================================
--- /branches/pap/Ohana/src/addstar/src/sky_tessalation.c	(revision 28483)
+++ /branches/pap/Ohana/src/addstar/src/sky_tessalation.c	(revision 28484)
@@ -940,5 +940,5 @@
 int dvo_image_clear_vtable (FITS_DB *db) {
 
-  int i;
+  int i, nbytes;
 
   // free memory used by the current vtable rows
@@ -951,5 +951,6 @@
 
   // reset db[0].theader(NAXIS1) to match Image
-  gfits_modify (&db[0].theader, "NAXIS1", "%lld", 1, (long long) sizeof(Image));
+  nbytes = sizeof(Image);
+  gfits_modify (&db[0].theader, "NAXIS1", "%d", 1,  nbytes);
   db[0].theader.Naxis[0] = sizeof(Image);
 
Index: /branches/pap/Ohana/src/addstar/test/dvomerge.dvo
===================================================================
--- /branches/pap/Ohana/src/addstar/test/dvomerge.dvo	(revision 28483)
+++ /branches/pap/Ohana/src/addstar/test/dvomerge.dvo	(revision 28484)
@@ -30,5 +30,8 @@
   exec rsync -auc catdir.test2/ catdir.test3/
 
+  date -var t1 -seconds -reftime 1276000000
   exec dvomerge catdir.test1 into catdir.test3
+  date -var t2 -seconds -reftime 1276000000
+  echo "merge time: {$t2 - $t1}"
 
   catdir catdir.test3
@@ -105,8 +108,84 @@
 end
 
-# create 2 populated catdirs, each with a couple of cmf files
-macro test.dvomerge.create
-
-  tapPLAN 21
+# create 1 populated catdir, merge into currently non-existent catdir
+macro test.dvomerge.update.new
+
+  tapPLAN 51
+
+  exec rm -rf catdir.test1
+  exec rm -rf catdir.test2
+
+  $RA = 10.0
+  $DEC = 20.0
+
+  mkinput
+  exec mkcmf test.in.txt test.cmf -date 2008/1/1 -time 01:00:00 -radec $RA $DEC
+  exec addstar -D CATDIR catdir.test1 -D CAMERA simtest test.cmf
+
+  exec mkcmf test.in.txt test.cmf -date 2008/1/1 -time 02:00:00 -radec $RA $DEC
+  exec addstar -D CATDIR catdir.test1 -D CAMERA simtest test.cmf
+
+  date -var t1 -seconds -reftime 1276000000
+  exec dvomerge catdir.test1 into catdir.test2
+  date -var t2 -seconds -reftime 1276000000
+  echo "merge time: {$t2 - $t1}"
+
+  catdir catdir.test2
+  skyregion {$RA-1} {$RA+1} {$DEC-1} {$DEC+1} 
+  mextract ra dec mag
+  create n 0 ra[]
+  subset r0 = ra if (n % 2 == 0)
+  subset r1 = ra if (n % 2 == 1)
+
+  catdir catdir.test1/
+  mextract RA DEC MAG
+  create N 0 RA[]
+  subset R0 = RA if (N % 2 == 0)
+  subset R1 = RA if (N % 2 == 1)
+
+  set dr0 = r0 - R0
+  vstat -q dr0
+  tapOK {abs($MEAN)  < 0.001} "ra (in - 0) vs ra (out - 0) (MEAN)"
+  tapOK {abs($SIGMA) < 0.001} "ra (in - 0) vs ra (out - 0) (SIGMA)"
+
+  set dr1 = r1 - R1
+  vstat -q dr1
+  tapOK {abs($MEAN)  < 0.001} "ra (in - 0) vs ra (out - 0) (MEAN)"
+  tapOK {abs($SIGMA) < 0.001} "ra (in - 0) vs ra (out - 0) (SIGMA)"
+
+  # check on updates to imageID
+  catdir catdir.test3
+  imextract imageID
+  sort imageID
+  tapOK {imageID[]  == 4} "image IDs exist"
+  tapOK {imageID[0] == 1} "updated image IDs"
+  tapOK {imageID[1] == 2} "updated image IDs"
+
+  catdir catdir.test2
+  mextract imageID, time
+  set id = imageID
+  set t = time
+  imextract imageID, time
+
+  for i 0 time[]
+    subset T = t if (id == imageID[$i])
+    set dT = T - time[$i]
+    vstat -q dT 
+    tapOK {abs($MEAN)  < 0.00001} "time for measure ID $i (MEAN)"
+    tapOK {abs($SIGMA) < 0.00001} "time for measure ID $i (SIGMA)"
+  end
+
+  # exec rm test.in.txt test.cmf
+  # exec rm -rf catdir.test1
+  # exec rm -rf catdir.test2
+  # exec rm -rf catdir.test3
+
+  tapDONE
+end
+
+# create 2 populated catdirs, each with a couple of cmf files -- force some unmatched objects 
+macro test.dvomerge.update.extras
+
+  tapPLAN 51
 
   exec rm -rf catdir.test1
@@ -124,4 +203,6 @@
   exec addstar -D CATDIR catdir.test1 -D CAMERA simtest test.cmf
 
+  # generate a few extra unmatched sources 
+  mkinput.extras
   exec mkcmf test.in.txt test.cmf -date 2008/1/1 -time 03:00:00 -radec $RA $DEC
   exec addstar -D CATDIR catdir.test2 -D CAMERA simtest test.cmf
@@ -130,5 +211,7 @@
   exec addstar -D CATDIR catdir.test2 -D CAMERA simtest test.cmf
 
-  exec dvomerge catdir.test1 and catdir.test2 to catdir.test3
+  exec rsync -auc catdir.test2/ catdir.test3/
+
+  exec dvomerge catdir.test1 into catdir.test3
 
   catdir catdir.test3
@@ -205,4 +288,104 @@
 end
 
+# create 2 populated catdirs, each with a couple of cmf files
+macro test.dvomerge.create
+
+  tapPLAN 21
+
+  exec rm -rf catdir.test1
+  exec rm -rf catdir.test2
+  exec rm -rf catdir.test3
+
+  $RA = 10.0
+  $DEC = 20.0
+
+  mkinput
+  exec mkcmf test.in.txt test.cmf -date 2008/1/1 -time 01:00:00 -radec $RA $DEC
+  exec addstar -D CATDIR catdir.test1 -D CAMERA simtest test.cmf
+
+  exec mkcmf test.in.txt test.cmf -date 2008/1/1 -time 02:00:00 -radec $RA $DEC
+  exec addstar -D CATDIR catdir.test1 -D CAMERA simtest test.cmf
+
+  exec mkcmf test.in.txt test.cmf -date 2008/1/1 -time 03:00:00 -radec $RA $DEC
+  exec addstar -D CATDIR catdir.test2 -D CAMERA simtest test.cmf
+
+  exec mkcmf test.in.txt test.cmf -date 2008/1/1 -time 04:00:00 -radec $RA $DEC
+  exec addstar -D CATDIR catdir.test2 -D CAMERA simtest test.cmf
+
+  exec dvomerge catdir.test1 and catdir.test2 to catdir.test3
+
+  catdir catdir.test3
+  skyregion {$RA-1} {$RA+1} {$DEC-1} {$DEC+1} 
+  mextract ra dec mag
+  create n 0 ra[]
+  subset r0 = ra if (n % 4 == 0)
+  subset r1 = ra if (n % 4 == 1)
+  subset r2 = ra if (n % 4 == 2)
+  subset r3 = ra if (n % 4 == 3)
+
+  catdir catdir.test1/
+  mextract RA DEC MAG
+  create N 0 RA[]
+  subset R0 = RA if (N % 2 == 0)
+  subset R1 = RA if (N % 2 == 1)
+
+  set dr0 = r0 - R0
+  vstat -q dr0
+  tapOK {abs($MEAN)  < 0.001} "ra (in - 0) vs ra (out - 0) (MEAN)"
+  tapOK {abs($SIGMA) < 0.001} "ra (in - 0) vs ra (out - 0) (SIGMA)"
+
+  set dr1 = r1 - R1
+  vstat -q dr1
+  tapOK {abs($MEAN)  < 0.001} "ra (in - 0) vs ra (out - 0) (MEAN)"
+  tapOK {abs($SIGMA) < 0.001} "ra (in - 0) vs ra (out - 0) (SIGMA)"
+
+  catdir catdir.test2/
+  mextract RA DEC MAG
+  create N 0 RA[]
+  subset R2 = RA if (N % 2 == 0)
+  subset R3 = RA if (N % 2 == 1)
+
+  set dr2 = r2 - R2
+  vstat -q dr2
+  tapOK {abs($MEAN)  < 0.001} "ra (in - 0) vs ra (out - 0) (MEAN)"
+  tapOK {abs($SIGMA) < 0.001} "ra (in - 0) vs ra (out - 0) (SIGMA)"
+
+  set dr3 = r3 - R3
+  vstat -q dr3
+  tapOK {abs($MEAN)  < 0.001} "ra (in - 0) vs ra (out - 0) (MEAN)"
+  tapOK {abs($SIGMA) < 0.001} "ra (in - 0) vs ra (out - 0) (SIGMA)"
+
+  # check on updates to imageID
+  catdir catdir.test3
+  imextract imageID
+  sort imageID
+  tapOK {imageID[]  == 4} "image IDs exist"
+  tapOK {imageID[0] == 1} "updated image IDs"
+  tapOK {imageID[1] == 2} "updated image IDs"
+  tapOK {imageID[2] == 3} "updated image IDs"
+  tapOK {imageID[3] == 4} "updated image IDs"
+
+  catdir catdir.test3
+  mextract imageID, time
+  set id = imageID
+  set t = time
+  imextract imageID, time
+
+  for i 0 time[]
+    subset T = t if (id == imageID[$i])
+    set dT = T - time[$i]
+    vstat -q dT 
+    tapOK {abs($MEAN)  < 0.00001} "time for measure ID $i (MEAN)"
+    tapOK {abs($SIGMA) < 0.00001} "time for measure ID $i (SIGMA)"
+  end
+
+  # exec rm test.in.txt test.cmf
+  # exec rm -rf catdir.test1
+  # exec rm -rf catdir.test2
+  # exec rm -rf catdir.test3
+
+  tapDONE
+end
+
 # make a simple input file for mkcmf
 macro mkinput.alt
@@ -225,4 +408,22 @@
   for i 10 1024 100
     for j 10 1024 100
+      fprintf " %4d %4d  %6.2f" $i $j {-15.0 + 2.5*($i + $j)/1000.0}
+    end
+  end
+  output stdout
+end
+
+# make a simple input file for mkcmf
+macro mkinput.extras
+  exec rm -f test.in.txt
+
+  output test.in.txt
+  for i 10 1024 100
+    for j 10 1024 100
+      fprintf " %4d %4d  %6.2f" $i $j {-15.0 + 2.5*($i + $j)/1000.0}
+    end
+  end
+  for i 20 1024 500
+    for j 20 1024 500
       fprintf " %4d %4d  %6.2f" $i $j {-15.0 + 2.5*($i + $j)/1000.0}
     end
Index: /branches/pap/Ohana/src/delstar/src/delete_imagename.c
===================================================================
--- /branches/pap/Ohana/src/delstar/src/delete_imagename.c	(revision 28483)
+++ /branches/pap/Ohana/src/delstar/src/delete_imagename.c	(revision 28484)
@@ -77,9 +77,9 @@
   Noutimage = Nimage - Nimlist;
   
-  if (VERBOSE) fprintf (stderr, "removing %lld images (leaving %lld of %lld)\n", (long long) Nimlist, (long long) Noutimage, (long long) Nimage);
+  if (VERBOSE) fprintf (stderr, "removing "OFF_T_FMT" images (leaving "OFF_T_FMT" of "OFF_T_FMT")\n",  Nimlist,  Noutimage,  Nimage);
   // gfits_table_set_Image (&db[0].ftable, outimage, Noutimage);
 
-  gfits_modify (&db[0].theader, "NAXIS2", "%lld", 1, (long long) Noutimage);
-  gfits_modify (&db[0].header, "NIMAGES", "%lld", 1, (long long) Noutimage);
+  gfits_modify (&db[0].theader, "NAXIS2", OFF_T_FMT, 1,  Noutimage);
+  gfits_modify (&db[0].header, "NIMAGES", OFF_T_FMT, 1,  Noutimage);
   db[0].theader.Naxis[1] = Noutimage;
   db[0].ftable.buffer = (char *) outimage;
Index: /branches/pap/Ohana/src/delstar/src/delete_missed.c
===================================================================
--- /branches/pap/Ohana/src/delstar/src/delete_missed.c	(revision 28483)
+++ /branches/pap/Ohana/src/delstar/src/delete_missed.c	(revision 28484)
@@ -11,5 +11,5 @@
   Nmiss = catalog[0].Nmissing;
   
-  if (VERBOSE) fprintf (stderr, "starting with Nave, Nmeas, Nmiss: %lld %lld %lld\n", (long long) Nave, (long long) Nmeas, (long long) Nmiss);
+  if (VERBOSE) fprintf (stderr, "starting with Nave, Nmeas, Nmiss: "OFF_T_FMT" "OFF_T_FMT" "OFF_T_FMT"\n",  Nave,  Nmeas,  Nmiss);
 
   /* set up references for missing to average */
@@ -19,5 +19,5 @@
   REALLOCATE (catalog[0].missing, Missing, 1);
   catalog[0].Nmissing = 0;
-  if (VERBOSE) fprintf (stderr, "  ending with Nave, Nmeas, Nmiss: %lld %lld %lld\n", (long long) Nave, (long long) Nmeas, (long long) Nmiss);
+  if (VERBOSE) fprintf (stderr, "  ending with Nave, Nmeas, Nmiss: "OFF_T_FMT" "OFF_T_FMT" "OFF_T_FMT"\n",  Nave,  Nmeas,  Nmiss);
 }
 
Index: /branches/pap/Ohana/src/delstar/src/find_matches.c
===================================================================
--- /branches/pap/Ohana/src/delstar/src/find_matches.c	(revision 28483)
+++ /branches/pap/Ohana/src/delstar/src/find_matches.c	(revision 28484)
@@ -22,5 +22,5 @@
   ALLOCATE (ave_miss, off_t, MAX(Nmiss,1));
   
-  if (VERBOSE) fprintf (stderr, "starting with Nave, Nmeas, Nmiss: %lld %lld %lld\n", (long long) Nave, (long long) Nmeas, (long long) Nmiss);
+  if (VERBOSE) fprintf (stderr, "starting with Nave, Nmeas, Nmiss: "OFF_T_FMT" "OFF_T_FMT" "OFF_T_FMT"\n",  Nave,  Nmeas,  Nmiss);
 
   /* set up pointers for linked list of measure */
@@ -75,5 +75,5 @@
       if (next_meas[j] != i) {
 	fprintf (stderr, "error? (1)  this link seems to have been lost\n");
-	fprintf (stderr, "j: %lld, next_meas[j]: %lld, i: %lld\n", (long long) j, (long long) next_meas[j], (long long) i);
+	fprintf (stderr, "j: "OFF_T_FMT", next_meas[j]: "OFF_T_FMT", i: "OFF_T_FMT"\n",  j,  next_meas[j],  i);
 	exit (1);
       }
@@ -108,5 +108,5 @@
 	if (next_miss[j] != m) {
 	  fprintf (stderr, "error? (2) this link seems to have been lost\n");
-	  fprintf (stderr, "j: %lld, next_miss[j]: %lld, i: %lld\n", (long long) j, (long long) next_miss[j], (long long) i);
+	  fprintf (stderr, "j: "OFF_T_FMT", next_miss[j]: "OFF_T_FMT", i: "OFF_T_FMT"\n",  j,  next_miss[j],  i);
 	  exit (1);
 	}
@@ -131,5 +131,5 @@
     }
   } 
-  fprintf (stderr, "found %lld meas to remove\n", (long long) Nmeasfound);
+  fprintf (stderr, "found "OFF_T_FMT" meas to remove\n",  Nmeasfound);
 
   if (VERBOSE) fprintf (stderr, "fixing missing..."); 
@@ -146,5 +146,5 @@
       if (next_miss[j] != i) {
 	fprintf (stderr, "error? (3) this link seems to have been lost\n");
-	fprintf (stderr, "j: %lld, next_miss[j]: %lld, i: %lld\n", (long long) j, (long long) next_miss[j], (long long) i);
+	fprintf (stderr, "j: "OFF_T_FMT", next_miss[j]: "OFF_T_FMT", i: "OFF_T_FMT"\n",  j,  next_miss[j],  i);
 	exit (1);
       }
@@ -249,5 +249,5 @@
   catalog[0].Nsecf_mem = Nave*Nsecfilt;
 
-  if (VERBOSE) fprintf (stderr, "  ending with Nave, Nmeas, Nmiss: %lld %lld %lld\n", (long long) Nave, (long long) Nmeas, (long long) Nmiss);
+  if (VERBOSE) fprintf (stderr, "  ending with Nave, Nmeas, Nmiss: "OFF_T_FMT" "OFF_T_FMT" "OFF_T_FMT"\n",  Nave,  Nmeas,  Nmiss);
 
   free (next_meas);
Index: /branches/pap/Ohana/src/dvomerge/src/LoadCatalog.c
===================================================================
--- /branches/pap/Ohana/src/dvomerge/src/LoadCatalog.c	(revision 28483)
+++ /branches/pap/Ohana/src/dvomerge/src/LoadCatalog.c	(revision 28484)
@@ -7,9 +7,6 @@
     catalog[0].Nsecfilt  = GetPhotcodeNsecfilt ();
 
-    if (!strcmp (mode, "w")) {
-	catalog[0].catflags = LOAD_NONE;
-    } else {
-	catalog[0].catflags = LOAD_AVES | LOAD_MEAS | LOAD_MISS | LOAD_SECF;
-    }
+    // always load all of the data (if any exists)
+    catalog[0].catflags = LOAD_AVES | LOAD_MEAS | LOAD_MISS | LOAD_SECF;
 
     catalog[0].catformat = dvo_catalog_catformat (CATFORMAT);  // set the default catformat from config data
Index: /branches/pap/Ohana/src/dvomerge/src/dvo_image_merge_dbs.c
===================================================================
--- /branches/pap/Ohana/src/dvomerge/src/dvo_image_merge_dbs.c	(revision 28483)
+++ /branches/pap/Ohana/src/dvomerge/src/dvo_image_merge_dbs.c	(revision 28484)
@@ -1,3 +1,5 @@
 # include "dvomerge.h"
+
+void sort_IDmap (IDmapType *IDmap);
 
 // merge db2 into db1
@@ -17,6 +19,6 @@
  /* adjust header */
   Nout = 0;
-  gfits_scan (&out[0].header, "NIMAGES", "%lld", 1, (long long *) &Nout);
-  status = gfits_scan (&out[0].header, "IMAGEID", "%lld", 1, (long long *) &IDstart);
+  gfits_scan (&out[0].header, "NIMAGES", OFF_T_FMT, 1,  &Nout);
+  status = gfits_scan (&out[0].header, "IMAGEID", OFF_T_FMT, 1,  &IDstart);
   if (!status) {
     IDstart = 1;
@@ -29,4 +31,7 @@
   }
 
+  // sort IDmap->old,new on the basis of IDmap->old:
+  sort_IDmap (IDmap);
+
   if (!out[0].swapped) {
     gfits_convert_Image ((Image *) out[0].ftable.buffer, sizeof(Image), Nout);
@@ -36,6 +41,6 @@
   Nout += Nimages;
   IDstart += Nimages;
-  gfits_modify (&out[0].header, "NIMAGES", "%lld", 1, (long long) Nout);
-  gfits_modify (&out[0].header, "IMAGEID", "%lld", 1, (long long) IDstart);
+  gfits_modify (&out[0].header, "NIMAGES", OFF_T_FMT, 1,  Nout);
+  gfits_modify (&out[0].header, "IMAGEID", OFF_T_FMT, 1,  IDstart);
 
   gfits_add_rows (&out[0].ftable, (char *) images, Nimages, sizeof(Image));
@@ -43,12 +48,32 @@
 }
 
-// optimize with sort and bisection
+// XXX isn't the map just ID_new = ID_old + offset ??
 off_t dvo_map_image_ID (IDmapType *IDmap, off_t oldID) {
 
-  off_t i;
+  // off_t i;
+  // 
+  // for (i = 0; i < IDmap->Nmap; i++) {
+  //   if (IDmap->old[i] != oldID) continue;
+  //   return (IDmap->new[i]);
+  // }
 
-  for (i = 0; i < IDmap->Nmap; i++) {
-    if (IDmap->old[i] != oldID) continue;
-    return (IDmap->new[i]);
+  off_t Nlo, Nhi, N;
+
+  // find the a close entry below desired ID
+  Nlo = 0; Nhi = IDmap->Nmap;
+  while (Nhi - Nlo > 10) {
+    N = 0.5*(Nlo + Nhi);
+    if (IDmap->old[N] < oldID) {
+      Nlo = MAX(N, 0);
+    } else {
+      Nhi = MIN(N + 1, IDmap->Nmap);
+    }
+  }
+
+  // search for the desired ID starting from Nlo, give up at Nhi
+  for (N = Nlo; N < Nhi; N++) { 
+    if (IDmap->old[N] < oldID) continue;
+    if (IDmap->old[N] > oldID) return 0;
+    return (IDmap->new[N]);
   }
   return 0;
@@ -65,5 +90,5 @@
     newID = dvo_map_image_ID (IDmap, oldID);
     if (newID == 0) {
-      fprintf (stderr, "cannot find image ID %lld\n", (long long) oldID);
+      fprintf (stderr, "cannot find image ID "OFF_T_FMT"\n",  oldID);
       exit (2);
     }
@@ -72,2 +97,19 @@
   return TRUE;
 }
+
+// sort two times vectors and an index by first time vector
+void sort_IDmap (IDmapType *IDmap) {
+
+# define SWAPFUNC(A,B){ off_t tmp_old, tmp_new;	   \
+  tmp_old = IDmap->old[A]; IDmap->old[A] = IDmap->old[B]; IDmap->old[B] = tmp_old; \
+  tmp_new = IDmap->new[A]; IDmap->new[A] = IDmap->new[B]; IDmap->new[B] = tmp_new; \
+}
+# define COMPARE(A,B)(IDmap->old[A] < IDmap->old[B])
+
+  OHANA_SORT (IDmap->Nmap, COMPARE, SWAPFUNC);
+
+# undef SWAPFUNC
+# undef COMPARE
+
+}
+
Index: /branches/pap/Ohana/src/dvomerge/src/dvoconvert.c
===================================================================
--- /branches/pap/Ohana/src/dvomerge/src/dvoconvert.c	(revision 28483)
+++ /branches/pap/Ohana/src/dvomerge/src/dvoconvert.c	(revision 28484)
@@ -150,7 +150,7 @@
 
   // update additional metadata
-  gfits_scan (&inDB.header, "IMAGEID", "%lld", 1, (long long *) &ID);
-  gfits_modify (&outDB.header, "NIMAGES", "%lld", 1, (long long) Nimages);
-  gfits_modify (&outDB.header, "IMAGEID", "%lld", 1, (long long) ID);
+  gfits_scan (&inDB.header, "IMAGEID", OFF_T_FMT, 1,  &ID);
+  gfits_modify (&outDB.header, "NIMAGES", OFF_T_FMT, 1,  Nimages);
+  gfits_modify (&outDB.header, "IMAGEID", OFF_T_FMT, 1,  ID);
 
   // copy input rows to output table
Index: /branches/pap/Ohana/src/dvomerge/src/dvomerge.c
===================================================================
--- /branches/pap/Ohana/src/dvomerge/src/dvomerge.c	(revision 28483)
+++ /branches/pap/Ohana/src/dvomerge/src/dvomerge.c	(revision 28484)
@@ -7,4 +7,6 @@
   ConfigInit (&argc, argv);
   dvomerge_args (&argc, argv);
+
+  // XXX require both inputs to be sorted?
 
   if (argc == 6) dvomergeCreate (argc, argv);
Index: /branches/pap/Ohana/src/dvomerge/src/dvomergeUpdate.c
===================================================================
--- /branches/pap/Ohana/src/dvomerge/src/dvomergeUpdate.c	(revision 28483)
+++ /branches/pap/Ohana/src/dvomerge/src/dvomergeUpdate.c	(revision 28484)
@@ -16,9 +16,23 @@
   output = argv[3];
 
-  // the first input defines the photcode table & db layout
-  sprintf (filename, "%s/Photcodes.dat", input);
+  // since we are merging the input db into the output db, the output defines the photcode
+  // table & db layout but, this requires the output to exist.  if it does not, instead use the
+  // input.
+  sprintf (filename, "%s/Photcodes.dat", output);
   if (!LoadPhotcodes (filename, NULL, FALSE)) {
-    fprintf (stderr, "error loading photcode table %s\n", filename);
-    exit (1);
+    sprintf (filename, "%s/Photcodes.dat", input);
+    if (!LoadPhotcodes (filename, NULL, FALSE)) {
+      fprintf (stderr, "error loading photcode table: tried %s/Photcodes.dat and %s/Photcodes.dat\n", output, input);
+      exit (1);
+    }
+    if (!check_dir_access (output, VERBOSE)) {
+      fprintf (stderr, "error creating output database directory %s\n", output);
+      exit (1);
+    }	
+    sprintf (filename, "%s/Photcodes.dat", output);
+    if (!SavePhotcodesFITS (filename)) {
+      fprintf (stderr, "error saving photcode table in %s/Photcodes.dat\n", output);
+      exit (1);
+    }	
   }
 
@@ -27,8 +41,14 @@
   // load the sky table for the existing database
   insky = SkyTableLoadOptimal (input, NULL, NULL, FALSE, SKY_DEPTH_HST, VERBOSE);
+  if (!insky) {
+      Shutdown ("can't read SkyTable for %s", input);
+  }
   SkyTableSetFilenames (insky, input, "cpt");
 
   // generate an output table populated at the desired depth
-  outsky = SkyTableLoadOptimal (output, NULL, NULL, TRUE, SKY_DEPTH, VERBOSE);
+  outsky = SkyTableLoadOptimal (output, NULL, GSCFILE, TRUE, SKY_DEPTH, VERBOSE);
+  if (!outsky) {
+      Shutdown ("can't read or create SkyTable for %s", output);
+  }
   SkyTableSetFilenames (outsky, output, "cpt");
 
@@ -43,11 +63,14 @@
     if (VERBOSE) fprintf (stderr, "output: %s\n", outsky[0].regions[i].name);
 
-    // load / create output catalog
-    LoadCatalog (&outcatalog, &outsky[0].regions[i], outsky[0].filename[i], "a");
+    // load / create output catalog (if catalog does not exist, it will be created)
+    LoadCatalog (&outcatalog, &outsky[0].regions[i], outsky[0].filename[i], "w");
 
     // combine only tables at equal or larger depth
       
     // load in all of the tables from input for this region
-    inlist = SkyListByBounds (insky, depth, outsky[0].regions[i].Rmin, outsky[0].regions[i].Rmax, outsky[0].regions[i].Dmin, outsky[0].regions[i].Dmax);
+    // SkyListByBounds will return neighbor catalogs if the boundaries exactly match (due to rounding).  Since the regions are not infinitely small, 
+    // compare to a slightly reduced footprint
+    float dPos = 2.0/3600.0;
+    inlist = SkyListByBounds (insky, depth, outsky[0].regions[i].Rmin + dPos, outsky[0].regions[i].Rmax - dPos, outsky[0].regions[i].Dmin + dPos, outsky[0].regions[i].Dmax - dPos);
     for (j = 0; j < inlist[0].Nregions; j++) {
       if (VERBOSE) fprintf (stderr, "input : %s\n", inlist[0].regions[j][0].name);
@@ -66,4 +89,6 @@
       dvo_catalog_unlock (&incatalog);
       dvo_catalog_free (&incatalog);
+
+      fprintf (stderr, "merged %s into %s\n", outsky[0].regions[i].name, inlist[0].regions[j][0].name);
     }
     SkyListFree (inlist);
@@ -102,8 +127,8 @@
   // load the image table 
   if (inDB.dbstate == LCK_EMPTY) {
-    Shutdown ("can't find input (1) image catalog %s", inDB.filename);
+    Shutdown ("can't find input image catalog %s", inDB.filename);
   }
   if (!dvo_image_load (&inDB, VERBOSE, TRUE)) {
-    Shutdown ("can't read input (1) image catalog %s", inDB.filename);
+    Shutdown ("can't read input image catalog %s", inDB.filename);
   }
 
@@ -117,8 +142,9 @@
   /* load the image table */
   if (outDB.dbstate == LCK_EMPTY) {
-    Shutdown ("can't find input (2) image catalog %s", outDB.filename);
-  }
-  if (!dvo_image_load (&outDB, VERBOSE, TRUE)) {
-    Shutdown ("can't read input (2) image catalog %s", outDB.filename);
+    dvo_image_create (&outDB, GetZeroPoint());
+  } else {
+    if (!dvo_image_load (&outDB, VERBOSE, TRUE)) {
+      Shutdown ("can't read output image catalog %s", outDB.filename);
+    }
   }
 
Index: /branches/pap/Ohana/src/dvomerge/src/dvoverify.c
===================================================================
--- /branches/pap/Ohana/src/dvomerge/src/dvoverify.c	(revision 28484)
+++ /branches/pap/Ohana/src/dvomerge/src/dvoverify.c	(revision 28484)
@@ -0,0 +1,173 @@
+# include "dvomerge.h"
+
+/* things we can verify easily:
+   table sizes: (NAXIS1 vs EXTTYPE; NAXIS2 vs data size)
+   sum of catalog.average.Nmeasure == catalog.Nmeasure
+   averef, obj_id consistent between average and measure
+   do we need a checksum?
+*/
+
+int main (int argc, char **argv) {
+
+  char filename[256], *input, *output;
+
+  int depth;
+  off_t i, j, Ns, Ne;
+  SkyTable *outsky, *insky;
+  SkyList *inlist;
+  Catalog incatalog, outcatalog;
+
+  SetSignals ();
+  dvoconvert_help (argc, argv);
+  ConfigInit (&argc, argv);
+  dvoconvert_args (&argc, argv);
+
+  if (strcasecmp (argv[2], "to")) dvoconvert_usage();
+  input = argv[1];
+  output = argv[3];
+
+  // load input images, save to output images
+  dvoConvert_copy_images (input, output);
+
+  // copy photcode table
+  { 
+    // the first input defines the photcode table & db layout
+    sprintf (filename, "%s/Photcodes.dat", input);
+    if (!LoadPhotcodes (filename, NULL, FALSE)) {
+      fprintf (stderr, "error loading photcode table %s\n", filename);
+      exit (1);
+    }
+    // save the photcodes in the output catdir
+    sprintf (filename, "%s/Photcodes.dat", output);
+    if (!check_file_access (filename, TRUE, TRUE, VERBOSE)) {
+      fprintf (stderr, "error creating output catdir %s\n", output);
+      exit (1);
+    }
+    if (!SavePhotcodesFITS (filename)) {
+      fprintf (stderr, "error saving photcode table %s\n", filename);
+      exit (1);
+    }
+  }
+
+  // copy skytable
+  { 
+    // load the sky table for the existing database
+    insky = SkyTableLoadOptimal (input, NULL, NULL, FALSE, SKY_DEPTH_HST, VERBOSE);
+    SkyTableSetFilenames (insky, input, "cpt");
+
+    // generate an output table populated at the desired depth
+    // XXX force this to match the input?
+    outsky = SkyTableLoadOptimal (output, NULL, GSCFILE, TRUE, SKY_DEPTH, VERBOSE);
+    SkyTableSetFilenames (outsky, output, "cpt");
+
+    SkyTablePopulatedRange (&Ns, &Ne, insky, 0);
+    depth = insky[0].regions[Ns].depth;
+    // XXX this seems to imply that insky is a uniform depth...
+  }
+
+  // loop over all input catalogs, save to output catalogs
+  // loop over the populatable output regions
+  for (i = 0; i < outsky[0].Nregions; i++) {
+    if (!outsky[0].regions[i].table) continue;
+    if (VERBOSE) fprintf (stderr, "output: %s\n", outsky[0].regions[i].name);
+
+    // load / create output catalog
+    LoadCatalog (&outcatalog, &outsky[0].regions[i], outsky[0].filename[i], "w");
+
+    // combine only tables at equal or larger depth
+      
+    // load in all of the tables from input for this region
+    inlist = SkyListByBounds (insky, depth, outsky[0].regions[i].Rmin, outsky[0].regions[i].Rmax, outsky[0].regions[i].Dmin, outsky[0].regions[i].Dmax);
+    for (j = 0; j < inlist[0].Nregions; j++) {
+      if (VERBOSE) fprintf (stderr, "input : %s\n", inlist[0].regions[j][0].name);
+
+      // load input catalog
+      LoadCatalog (&incatalog, inlist[0].regions[j], inlist[0].filename[j], "r");
+
+      // skip empty input catalogs
+      if (!incatalog.Naves_disk) {
+	dvo_catalog_unlock (&incatalog);
+	dvo_catalog_free (&incatalog);
+	continue;
+      }
+      merge_catalogs_new (&outsky[0].regions[i], &outcatalog, &incatalog);
+      dvo_catalog_unlock (&incatalog);
+      dvo_catalog_free (&incatalog);
+    }
+    SkyListFree (inlist);
+
+    outcatalog.catflags = LOAD_AVES | LOAD_MEAS | LOAD_MISS | LOAD_SECF;
+    dvo_catalog_save (&outcatalog, VERBOSE);
+    dvo_catalog_unlock (&outcatalog);
+    dvo_catalog_free (&outcatalog);
+  }
+
+  // save the output sky table copy
+  sprintf (filename, "%s/SkyTable.fits", output);
+  check_file_access (filename, TRUE, TRUE, VERBOSE);
+  if (!SkyTableSave (outsky, filename)) {
+    fprintf (stderr, "ERROR: failed to save sky table for %s\n", output);
+    exit (1);
+  }
+
+  exit (0);
+}
+
+int dvoConvert_copy_images (char *input, char *output) {
+
+  FITS_DB inDB;
+  FITS_DB outDB;
+  int    status;
+
+  Image *images;
+  off_t Nimages;
+  off_t ID;
+
+  /*** load output/Images.dat ***/
+  sprintf (ImageCat, "%s/Images.dat", output);
+  outDB.mode   = dvo_catalog_catmode (CATMODE);
+  outDB.format = dvo_catalog_catformat (CATFORMAT);
+  status       = dvo_image_lock (&outDB, ImageCat, 3600.0, LCK_XCLD);  // shorter timeout?
+  if (!status) Shutdown ("ERROR: failure to lock image catalog %s", outDB.filename);
+
+  // output image table should not already exist
+  if (outDB.dbstate != LCK_EMPTY) {
+    Shutdown ("ERROR: image table %s already exists", outDB.filename);
+  }
+  dvo_image_create (&outDB, GetZeroPoint());
+
+  /*** load input/Images.dat ***/
+  sprintf (ImageCat, "%s/Images.dat", input);
+  // inDB.mode   = dvo_catalog_catmode (CATMODE);
+  // inDB.format = dvo_catalog_catformat (CATFORMAT);
+  status       = dvo_image_lock (&inDB, ImageCat, 3600.0, LCK_XCLD);  // shorter timeout?
+  if (!status) Shutdown ("ERROR: failure to lock image catalog %s", inDB.filename);
+
+  // load the image table 
+  if (inDB.dbstate == LCK_EMPTY) {
+    Shutdown ("can't find input image catalog %s", inDB.filename);
+  }
+  if (!dvo_image_load (&inDB, VERBOSE, TRUE)) {
+    Shutdown ("can't read input image catalog %s", inDB.filename);
+  }
+
+  // convert the raw image table to Image type (byteswap if needed)
+  images = gfits_table_get_Image (&inDB.ftable, &Nimages, &inDB.swapped);
+
+  // update additional metadata
+  gfits_scan (&inDB.header, "IMAGEID", OFF_T_FMT, 1,  &ID);
+  gfits_modify (&outDB.header, "NIMAGES", OFF_T_FMT, 1,  Nimages);
+  gfits_modify (&outDB.header, "IMAGEID", OFF_T_FMT, 1,  ID);
+
+  // copy input rows to output table
+  gfits_add_rows (&outDB.ftable, (char *) images, Nimages, sizeof(Image));
+
+  // write out the image table to disk
+  SetProtect (TRUE);
+  dvo_image_save (&outDB, VERBOSE);
+  SetProtect (FALSE);
+  dvo_image_unlock (&outDB); // unlock output
+  dvo_image_unlock (&inDB); // unlock input1
+
+  return TRUE;
+}
Index: /branches/pap/Ohana/src/dvomerge/src/merge_catalogs_new.c
===================================================================
--- /branches/pap/Ohana/src/dvomerge/src/merge_catalogs_new.c	(revision 28483)
+++ /branches/pap/Ohana/src/dvomerge/src/merge_catalogs_new.c	(revision 28484)
@@ -72,8 +72,8 @@
   
   if (VERBOSE) {
-      fprintf (stderr, "%lld: using %lld stars (%lld measures) for catalog\n", 
-	       (long long) i, 
-	       (long long) output[0].Naverage, 
-	       (long long) output[0].Nmeasure);
+      fprintf (stderr, OFF_T_FMT": using "OFF_T_FMT" stars ("OFF_T_FMT" measures) for catalog\n", 
+	        i, 
+	        output[0].Naverage, 
+	        output[0].Nmeasure);
   }
   return (TRUE);
Index: /branches/pap/Ohana/src/dvomerge/src/merge_catalogs_old.c
===================================================================
--- /branches/pap/Ohana/src/dvomerge/src/merge_catalogs_old.c	(revision 28483)
+++ /branches/pap/Ohana/src/dvomerge/src/merge_catalogs_old.c	(revision 28484)
@@ -2,7 +2,15 @@
 # define PSPS_ID TRUE
 
-# define IN_REGION(R,D) ( \
-((D) >= region[0].Dmin) && ((D) < region[0].Dmax) && \
-((R) >= region[0].Rmin)  && ((R) < region[0].Rmax))
+# define MARKTIME(MSG,...) {			\
+    float dtime;				\
+    gettimeofday (&stop, (void *) NULL);	\
+    dtime = DTIME (stop, start);		\
+    fprintf (stderr, MSG, __VA_ARGS__);		\
+    gettimeofday (&start, (void *) NULL);	\
+  }
+
+# define IN_REGION(R,D) (					\
+    ((D) >= region[0].Dmin) && ((D) < region[0].Dmax) &&	\
+    ((R) >= region[0].Rmin)  && ((R) < region[0].Rmax))
 
 // merge the input data into the output catalog
@@ -10,5 +18,5 @@
 int merge_catalogs_old (SkyRegion *region, Catalog *output, Catalog *input, double RADIUS) {
 
-  off_t i, j, Nin, offset, J, Jmin, status, Nstars;
+  off_t i, j, k, Nin, offset, J, Jmin, status, Nstars;
   double RADIUS2, Rmin, Rin, Din;
   double *X1, *Y1, *X2, *Y2;
@@ -19,4 +27,7 @@
   unsigned int objID, catID;
   Coords tcoords;
+
+  // struct timeval start, stop;
+  // gettimeofday (&start, (void *) NULL);
 
   Nsecfilt = output[0].Nsecfilt;
@@ -64,4 +75,6 @@
   strcpy (tcoords.ctype, "RA---ARC");
 
+  if (VERBOSE) fprintf (stderr, "merging %s into %s\n", input[0].filename, output[0].filename);
+
   /* build spatial index (RA sort) referencing input array sequence */
   Nstars = 0;
@@ -96,5 +109,5 @@
 
   /* set up pointers for linked list of measure */
-  if (output[0].sorted && (output[0].Nmeasure == output[0].Nmeas_disk)) {
+  if (output[0].sorted && (output[0].Nmeasure >= output[0].Nmeas_disk)) {
     // this version is only valid if we have done a full catalog load, and if the catalog
     // is sorted while processed
@@ -106,4 +119,6 @@
   /* choose a radius for matches */
   RADIUS2 = RADIUS*RADIUS;
+
+  // MARKTIME("set up structures: %f sec\n", dtime);
 
   /** find matched stars **/
@@ -162,4 +177,6 @@
     }
 
+    // 4) average properties from the input and the output db need to be properly merged.
+
     /** add ALL measurements for this input average object **/
     for (Nin = 0; Nin < input[0].average[N].Nmeasure; Nin ++) {
@@ -183,5 +200,7 @@
       output[0].measure[Nmeas].catID    = output[0].catID;
 
-      // fprintf (stderr, "Nave : %lld, Nmeas : %lld, dR: %f, dD: %f, catID: %d\n", (long long) n, (long long) Nmeas, output[0].measure[Nmeas].dR, output[0].measure[Nmeas].dD, output[0].measure[i].catID);
+      assert (output[0].measure[Nmeas].averef < Nave);
+
+      // fprintf (stderr, "Nave : "OFF_T_FMT", Nmeas : "OFF_T_FMT", dR: %f, dD: %f, catID: %d\n",  n,  Nmeas, output[0].measure[Nmeas].dR, output[0].measure[Nmeas].dD, output[0].measure[i].catID);
 
       // rationalize dR
@@ -207,4 +226,12 @@
     }
 
+    // update the average properties to reflect the incoming entries:
+    // if the original value is NAN but the input value is not, accept the input:
+    for (k = 0; k < Nsecfilt; k++) {
+      if ( isfinite(output[0].secfilt[n*Nsecfilt+k].M)) continue;
+      if (!isfinite( input[0].secfilt[N*Nsecfilt+k].M)) continue;
+      output[0].secfilt[n*Nsecfilt+k] = input[0].secfilt[N*Nsecfilt+k];
+    }
+
     /* Nm is updated, but not written out in -update mode (for existing entries)
        Nm is recalculated in build_meas_links if loaded table is not sorted */
@@ -212,11 +239,5 @@
     i++;
   }
-
-# if (0)
-  fprintf (stderr, "--- 1 ---\n");
-  for (i = 0; i < Nmeas; i++) {
-    fprintf (stderr, "Nave : %d, Nmeas : %lld, dR: %f, dD: %f, catID: %d\n", output[0].measure[i].averef, (long long) i, output[0].measure[i].dR, output[0].measure[i].dD, output[0].measure[i].catID);
-  }
-# endif
+  // MARKTIME("find matched stars: %f sec for "OFF_T_FMT","OFF_T_FMT" stars\n", dtime, Nstars, Nave);
 
   /** incorporate unmatched image stars, if this star is in field of this catalog **/
@@ -240,4 +261,6 @@
     if (!IN_REGION (input[0].average[N].R, input[0].average[N].D)) continue;
 
+    // XXX should we accept the input measurements for these fields?
+
     output[0].average[Nave].R         	   = input[0].average[N].R;
     output[0].average[Nave].D         	   = input[0].average[N].D;
@@ -245,5 +268,5 @@
     output[0].average[Nave].dD        	   = 0;
 
-    output[0].average[Nave].Nmeasure  	   = 1;
+    output[0].average[Nave].Nmeasure  	   = 0; // this value is update as the measurements are associated with this entry below
     output[0].average[Nave].Nmissing  	   = 0;
     output[0].average[Nave].Nextend        = 0;
@@ -280,11 +303,21 @@
 
     for (j = 0; j < Nsecfilt; j++) {
-      output[0].secfilt[Nave*Nsecfilt+j].M  = NAN;
-      output[0].secfilt[Nave*Nsecfilt+j].dM = NAN;
-      output[0].secfilt[Nave*Nsecfilt+j].Xm = NAN_S_SHORT;
-      output[0].secfilt[Nave*Nsecfilt+j].M_20 	= NAN_S_SHORT;
-      output[0].secfilt[Nave*Nsecfilt+j].M_80 	= NAN_S_SHORT;
-      output[0].secfilt[Nave*Nsecfilt+j].Ncode = 0;
-      output[0].secfilt[Nave*Nsecfilt+j].Nused = 0;
+      if (isfinite(input[0].secfilt[N*Nsecfilt+j].M)) {
+	output[0].secfilt[Nave*Nsecfilt+j].M     = input[0].secfilt[N*Nsecfilt+j].M;
+	output[0].secfilt[Nave*Nsecfilt+j].dM    = input[0].secfilt[N*Nsecfilt+j].dM;
+	output[0].secfilt[Nave*Nsecfilt+j].Xm    = input[0].secfilt[N*Nsecfilt+j].Xm;
+	output[0].secfilt[Nave*Nsecfilt+j].M_20  = input[0].secfilt[N*Nsecfilt+j].M_20;
+	output[0].secfilt[Nave*Nsecfilt+j].M_80  = input[0].secfilt[N*Nsecfilt+j].M_80;
+	output[0].secfilt[Nave*Nsecfilt+j].Ncode = input[0].secfilt[N*Nsecfilt+j].Ncode;
+	output[0].secfilt[Nave*Nsecfilt+j].Nused = input[0].secfilt[N*Nsecfilt+j].Nused;
+      } else {
+	output[0].secfilt[Nave*Nsecfilt+j].M     = NAN;
+	output[0].secfilt[Nave*Nsecfilt+j].dM    = NAN;
+	output[0].secfilt[Nave*Nsecfilt+j].Xm    = NAN_S_SHORT;
+	output[0].secfilt[Nave*Nsecfilt+j].M_20  = NAN_S_SHORT;
+	output[0].secfilt[Nave*Nsecfilt+j].M_80  = NAN_S_SHORT;
+	output[0].secfilt[Nave*Nsecfilt+j].Ncode = 0;
+	output[0].secfilt[Nave*Nsecfilt+j].Nused = 0;
+      }
     }
 
@@ -302,6 +335,8 @@
       output[0].measure[Nmeas].catID    = output[0].catID;
 
-      /* next[Nmeas] should always be -1 in this context (it is always the only
-	 measurement for the star) */
+      // as we add measurements, update Nmeasure to match
+      output[0].average[Nave].Nmeasure ++;
+
+      /* we set next[Nmeas] to -1 here, and update correctly below */
       input[0].found[N] = Nmeas;
       next_meas[Nmeas] = -1;
@@ -310,20 +345,15 @@
     int Ngroup = input[0].average[N].Nmeasure;
     for (j = 0; j < Ngroup - 1; j++) {
-	next_meas[Nmeas - Ngroup + j] = Nmeas - Ngroup + j + 1;
+      next_meas[Nmeas - Ngroup + j] = Nmeas - Ngroup + j + 1;
     }
     Nave ++;
   }
       
+  // MARKTIME("save unmatched stars: %f sec\n", dtime);
+
   REALLOCATE (output[0].average, Average, Nave);
   REALLOCATE (output[0].measure, Measure, Nmeas);
  
-# if (0)
-  fprintf (stderr, "--- 2 ---\n");
-  for (i = 0; i < Nmeas; i++) {
-    fprintf (stderr, "Nave : %d, Nmeas : %lld, dR: %f, dD: %f, catID: %d\n", output[0].measure[i].averef, (long long) i, output[0].measure[i].dR, output[0].measure[i].dD, output[0].measure[i].catID);
-  }
-# endif
-
-# define NOSORT FALSE
+# define NOSORT 0
   if (NOSORT) {
     output[0].sorted = FALSE;
@@ -332,11 +362,4 @@
     output[0].measure = sort_measure (output[0].average, Nave, output[0].measure, Nmeas, next_meas);
   }
-
-# if (0)
-  fprintf (stderr, "--- 3 ---\n");
-  for (i = 0; i < Nmeas; i++) {
-    fprintf (stderr, "Nave : %d, Nmeas : %lld, dR: %f, dD: %f, catID: %d\n", output[0].measure[i].averef, (long long) i, output[0].measure[i].dR, output[0].measure[i].dD, output[0].measure[i].catID);
-  }
-# endif
 
   /* note stars which have been found in this catalog */
@@ -354,5 +377,5 @@
   output[0].Nmeasure = Nmeas;
   output[0].Nsecf_mem = Nave*Nsecfilt;
-  if (VERBOSE) fprintf (stderr, "Nstars, Nave, Nmeas: %lld %lld %lld, (%lld matches)\n", (long long) Nstars, (long long) Nave, (long long) Nmeas, (long long) Nmatch);
+  if (VERBOSE) fprintf (stderr, "Nstars, Nave, Nmeas: "OFF_T_FMT" "OFF_T_FMT" "OFF_T_FMT", ("OFF_T_FMT" matches)\n",  Nstars,  Nave,  Nmeas,  Nmatch);
 
   free (next_meas);
@@ -366,4 +389,6 @@
   free (Y1);
   free (N1);
+
+  // MARKTIME("cleanup: %f sec\n", dtime);
   return (Nmatch);
 }
Index: /branches/pap/Ohana/src/fixcat/src/wcatalog.c
===================================================================
--- /branches/pap/Ohana/src/fixcat/src/wcatalog.c	(revision 28483)
+++ /branches/pap/Ohana/src/fixcat/src/wcatalog.c	(revision 28484)
@@ -34,7 +34,7 @@
   chmod (filename, mode);
   
-  gfits_modify (&catalog[0].header, "NSTARS", "%lld", 1, catalog[0].Naverage);
-  gfits_modify (&catalog[0].header, "NMEAS",  "%lld", 1, catalog[0].Nmeasure);
-  gfits_modify (&catalog[0].header, "NMISS",  "%lld", 1, catalog[0].Nmissing);
+  gfits_modify (&catalog[0].header, "NSTARS", OFF_T_FMT, 1, catalog[0].Naverage);
+  gfits_modify (&catalog[0].header, "NMEAS",  OFF_T_FMT, 1, catalog[0].Nmeasure);
+  gfits_modify (&catalog[0].header, "NMISS",  OFF_T_FMT, 1, catalog[0].Nmissing);
 
   gfits_modify_alt (&catalog[0].header, "MARKSTAR", "%t", 1, TRUE);
Index: /branches/pap/Ohana/src/getstar/src/MatchImages.c
===================================================================
--- /branches/pap/Ohana/src/getstar/src/MatchImages.c	(revision 28483)
+++ /branches/pap/Ohana/src/getstar/src/MatchImages.c	(revision 28484)
@@ -132,5 +132,5 @@
   }
   
-  if (VERBOSE) fprintf (stderr, "found %lld overlapping images\n", (long long) nmatch);
+  if (VERBOSE) fprintf (stderr, "found "OFF_T_FMT" overlapping images\n",  nmatch);
 
   *Nmatch = nmatch;
Index: /branches/pap/Ohana/src/getstar/src/SelectImages.c
===================================================================
--- /branches/pap/Ohana/src/getstar/src/SelectImages.c	(revision 28483)
+++ /branches/pap/Ohana/src/getstar/src/SelectImages.c	(revision 28484)
@@ -31,5 +31,5 @@
   }  
 
-  if (VERBOSE) fprintf (stderr, "found %lld matching images\n", (long long) nmatch);
+  if (VERBOSE) fprintf (stderr, "found "OFF_T_FMT" matching images\n",  nmatch);
 
   *Nmatch = nmatch;
Index: /branches/pap/Ohana/src/getstar/src/select_by_region.c
===================================================================
--- /branches/pap/Ohana/src/getstar/src/select_by_region.c	(revision 28483)
+++ /branches/pap/Ohana/src/getstar/src/select_by_region.c	(revision 28484)
@@ -161,6 +161,6 @@
   output[0].Nsecf_mem = Nave*Nsecfilt;
 
-  fprintf (stderr, "output catalog has %lld stars (%lld measures, %d secfilt)\n",
-           (long long) output[0].Naverage, (long long) output[0].Nmeasure, output[0].Nsecfilt);
+  fprintf (stderr, "output catalog has "OFF_T_FMT" stars ("OFF_T_FMT" measures, %d secfilt)\n",
+            output[0].Naverage,  output[0].Nmeasure, output[0].Nsecfilt);
   return (TRUE);
 }
Index: /branches/pap/Ohana/src/imregister/detrend/delete.c
===================================================================
--- /branches/pap/Ohana/src/imregister/detrend/delete.c	(revision 28483)
+++ /branches/pap/Ohana/src/imregister/detrend/delete.c	(revision 28484)
@@ -10,5 +10,5 @@
   ALLOCATE (keep, off_t, MAX (Nimage, 1));
   for (i = 0; i < Nimage; i++) keep[i] = TRUE;
-  fprintf (stderr, "total of %lld detrend images\n", (long long) Nimage);
+  fprintf (stderr, "total of "OFF_T_FMT" detrend images\n",  Nimage);
 
   Ndel = 0;
@@ -20,5 +20,5 @@
     delete_image (&image[i]);
   }
-  fprintf (stderr, "delete %lld images\n", (long long) Ndel);
+  fprintf (stderr, "delete "OFF_T_FMT" images\n",  Ndel);
   if (Ndel == 0) { 
     fprintf (stderr, "SUCCESS\n");
@@ -30,5 +30,5 @@
   Nsubset = Nimage - Ndel;
   ALLOCATE (subset, DetReg, MAX (1, Nsubset));
-  fprintf (stderr, "keeping %lld images\n", (long long) Nsubset);
+  fprintf (stderr, "keeping "OFF_T_FMT" images\n",  Nsubset);
   for (j = i = 0; i < Nimage; i++) {
     if (!keep[i]) continue;
Index: /branches/pap/Ohana/src/imregister/imphot/rtext.c
===================================================================
--- /branches/pap/Ohana/src/imregister/imphot/rtext.c	(revision 28483)
+++ /branches/pap/Ohana/src/imregister/imphot/rtext.c	(revision 28484)
@@ -10,5 +10,5 @@
   /* check that file size makes sense */
   Nimage = 0;
-  gfits_scan (&db[0].header, "NIMAGES", "%lld", 1, (long long *) &Nimage);
+  gfits_scan (&db[0].header, "NIMAGES", OFF_T_FMT, 1,  &Nimage);
   if (stat (db[0].filename, &filestatus) == -1) {
     if (VERBOSE) fprintf (stderr, "ERROR: failed to get status of image catalog\n");
@@ -21,5 +21,5 @@
     Ndata = (filestatus.st_size - db[0].header.datasize) / sizeof (Image);
     if (VERBOSE) fprintf (stderr, "ERROR: image catalog has inconsistent size\n");
-    if (VERBOSE) fprintf (stderr, "header: %lld, data: %d\n", (long long) Nimage, Ndata);
+    if (VERBOSE) fprintf (stderr, "header: "OFF_T_FMT", data: %d\n",  Nimage, Ndata);
     if (!FORCE_READ) exit (1);
     Nimage = Ndata;
@@ -41,5 +41,5 @@
   } 
   db[0].ftable.buffer = (char *) image;
-  gfits_modify (&db[0].theader, "NAXIS2", "%lld", 1, (long long) Nimage);
+  gfits_modify (&db[0].theader, "NAXIS2", OFF_T_FMT, 1,  Nimage);
   db[0].theader.Naxis[1] = Nimage;
   db[0].ftable.datasize = gfits_data_size (&db[0].theader);
Index: /branches/pap/Ohana/src/imregister/imreg/delete.c
===================================================================
--- /branches/pap/Ohana/src/imregister/imreg/delete.c	(revision 28483)
+++ /branches/pap/Ohana/src/imregister/imreg/delete.c	(revision 28484)
@@ -10,5 +10,5 @@
   ALLOCATE (keep, off_t, MAX (Nimage, 1));
   for (i = 0; i < Nimage; i++) keep[i] = TRUE;
-  fprintf (stderr, "total of %lld images\n", (long long) Nimage);
+  fprintf (stderr, "total of "OFF_T_FMT" images\n",  Nimage);
 
   Ndel = 0;
@@ -19,5 +19,5 @@
     Ndel ++;
   }
-  fprintf (stderr, "delete %lld images\n", (long long) Ndel);
+  fprintf (stderr, "delete "OFF_T_FMT" images\n",  Ndel);
   if (Ndel == 0) { 
     fprintf (stderr, "SUCCESS\n");
@@ -29,5 +29,5 @@
   Nsubset = Nimage - Ndel;
   ALLOCATE (subset, RegImage, MAX (1, Nsubset));
-  fprintf (stderr, "keeping %lld images\n", (long long) Nsubset);
+  fprintf (stderr, "keeping "OFF_T_FMT" images\n",  Nsubset);
   for (j = i = 0; i < Nimage; i++) {
     if (!keep[i]) continue;
Index: /branches/pap/Ohana/src/imregister/photreg/delete.c
===================================================================
--- /branches/pap/Ohana/src/imregister/photreg/delete.c	(revision 28483)
+++ /branches/pap/Ohana/src/imregister/photreg/delete.c	(revision 28484)
@@ -10,5 +10,5 @@
   ALLOCATE (keep, off_t, MAX (Nphotdata, 1));
   for (i = 0; i < Nphotdata; i++) keep[i] = TRUE;
-  fprintf (stderr, "total of %lld photdata\n", (long long) Nphotdata);
+  fprintf (stderr, "total of "OFF_T_FMT" photdata\n",  Nphotdata);
 
   Ndel = 0;
@@ -19,5 +19,5 @@
     Ndel ++;
   }
-  fprintf (stderr, "delete %lld photdata\n", (long long) Ndel);
+  fprintf (stderr, "delete "OFF_T_FMT" photdata\n",  Ndel);
 
   if (Ndel == 0) { 
@@ -31,5 +31,5 @@
   Nsubset = Nphotdata - Ndel;
   ALLOCATE (subset, PhotPars, MAX (1, Nsubset));
-  fprintf (stderr, "keeping %lld photdata\n", (long long) Nsubset);
+  fprintf (stderr, "keeping "OFF_T_FMT" photdata\n",  Nsubset);
   for (j = i = 0; i < Nphotdata; i++) {
     if (!keep[i]) continue;
Index: /branches/pap/Ohana/src/imregister/spreg/delete.c
===================================================================
--- /branches/pap/Ohana/src/imregister/spreg/delete.c	(revision 28483)
+++ /branches/pap/Ohana/src/imregister/spreg/delete.c	(revision 28484)
@@ -10,5 +10,5 @@
   ALLOCATE (keep, off_t, MAX (Nspectrum, 1));
   for (i = 0; i < Nspectrum; i++) keep[i] = TRUE;
-  fprintf (stderr, "total of %lld spectra\n", (long long) Nspectrum);
+  fprintf (stderr, "total of "OFF_T_FMT" spectra\n",  Nspectrum);
 
   Nbad = 0;
@@ -19,5 +19,5 @@
     Nbad ++;
   }
-  fprintf (stderr, "delete %lld spectra\n", (long long) Nbad);
+  fprintf (stderr, "delete "OFF_T_FMT" spectra\n",  Nbad);
   if (Nbad == 0) { 
     fprintf (stderr, "SUCCESS\n");
@@ -28,5 +28,5 @@
   Nsubset = Nspectrum - Nbad;
   ALLOCATE (subset, Spectrum, MAX (1, Nsubset));
-  fprintf (stderr, "keeping %lld spectra\n", (long long) Nsubset);
+  fprintf (stderr, "keeping "OFF_T_FMT" spectra\n",  Nsubset);
   for (j = i = 0; i < Nspectrum; i++) {
     if (!keep[i]) continue;
Index: /branches/pap/Ohana/src/imregister/src/convertimreg.c
===================================================================
--- /branches/pap/Ohana/src/imregister/src/convertimreg.c	(revision 28483)
+++ /branches/pap/Ohana/src/imregister/src/convertimreg.c	(revision 28484)
@@ -41,9 +41,9 @@
 
   /* load existing data from database */
-  gfits_scan (&header, "NIMAGES", "%lld", 1, (long long *) &Nimage);
+  gfits_scan (&header, "NIMAGES", OFF_T_FMT, 1,  &Nimage);
   ALLOCATE (pimage, RegImage, Nimage);
   status = fread (pimage, sizeof(RegImage), Nimage, f);
   if (status != Nimage) {
-    fprintf (stderr, "ERROR: header and data in dB don't match (%lld vs %lld)\n", (long long) Nimage, (long long) status);
+    fprintf (stderr, "ERROR: header and data in dB don't match ("OFF_T_FMT" vs "OFF_T_FMT")\n",  Nimage,  status);
     fclearlockfile (argv[1], f, lockstate, &dbstate);
     exit (1);
Index: /branches/pap/Ohana/src/imregister/src/imphotmerge.c
===================================================================
--- /branches/pap/Ohana/src/imregister/src/imphotmerge.c	(revision 28483)
+++ /branches/pap/Ohana/src/imregister/src/imphotmerge.c	(revision 28484)
@@ -113,5 +113,5 @@
   }
   Nimage += Nin;
-  gfits_modify (&header, "NIMAGES", "%lld", 1, (long long) Nimage);
+  gfits_modify (&header, "NIMAGES", OFF_T_FMT, 1,  Nimage);
 
   /* position to begining of file to write header */
@@ -152,5 +152,5 @@
   /* check that file size makes sense */
   Nimage = 0;
-  gfits_scan (header, "NIMAGES", "%lld", 1, (long long *) &Nimage);
+  gfits_scan (header, "NIMAGES", OFF_T_FMT, 1,  &Nimage);
   if (fstat (fileno(f), &filestatus) == -1) {
     fprintf (stderr, "ERROR: failed to get status of image catalog\n");
@@ -161,5 +161,5 @@
     Ndata = (filestatus.st_size - header[0].datasize) / sizeof (Image);
     fprintf (stderr, "ERROR: image catalog has inconsistent size\n");
-    fprintf (stderr, "header: %lld, data: %lld\n", (long long) Nimage, (long long) Ndata);
+    fprintf (stderr, "header: "OFF_T_FMT", data: "OFF_T_FMT"\n",  Nimage,  Ndata);
     Nimage = Ndata;
   } 
Index: /branches/pap/Ohana/src/kapa2/src/LoadPicture.c
===================================================================
--- /branches/pap/Ohana/src/kapa2/src/LoadPicture.c	(revision 28483)
+++ /branches/pap/Ohana/src/kapa2/src/LoadPicture.c	(revision 28484)
@@ -32,5 +32,5 @@
   gfits_init_header (&header);
   header.Naxes = 2;
-  KiiScanMessage (sock, "%lld %lld", (long long *) &header.Naxis[0], (long long *) &header.Naxis[1]);
+  KiiScanMessage (sock, OFF_T_FMT" "OFF_T_FMT,  &header.Naxis[0],  &header.Naxis[1]);
 
   // internal image are 32 bit floats; sender must send in this format
@@ -41,5 +41,5 @@
 
   KiiScanMessage (sock, "%lf %lf %s %s",  &image[0].image[0].zero, &image[0].image[0].range, image[0].image[0].name, image[0].image[0].file);
-  KiiScanMessage (sock, "%lf %lf %lld", &image[0].image[0].min,  &image[0].image[0].max, (long long *) &header.datasize);
+  KiiScanMessage (sock, "%lf %lf "OFF_T_FMT, &image[0].image[0].min,  &image[0].image[0].max,  &header.datasize);
   KiiScanMessage (sock, "%lf %f %f %f %f", &image[0].image[0].coords.crval1, &image[0].image[0].coords.crpix1, &image[0].image[0].coords.cdelt1, &image[0].image[0].coords.pc1_1, &image[0].image[0].coords.pc1_2);
   KiiScanMessage (sock, "%lf %f %f %f %f", &image[0].image[0].coords.crval2, &image[0].image[0].coords.crpix2, &image[0].image[0].coords.cdelt2, &image[0].image[0].coords.pc2_1, &image[0].image[0].coords.pc2_2);
@@ -85,8 +85,8 @@
   fcntl (sock, F_SETFL, !O_NONBLOCK);  
 
-  if (DEBUG) fprintf (stderr, "read %lld bytes\n", (long long) image[0].image[0].matrix.datasize);
+  if (DEBUG) fprintf (stderr, "read "OFF_T_FMT" bytes\n",  image[0].image[0].matrix.datasize);
   /* it it not obvious this condition should kill kapa, but ... */
   if (image[0].image[0].matrix.datasize != header.datasize) {  
-    fprintf (stderr, "error: expected %lld bytes, but got only %lld\n", (long long) header.datasize, (long long) image[0].image[0].matrix.datasize);
+    fprintf (stderr, "error: expected "OFF_T_FMT" bytes, but got only "OFF_T_FMT"\n",  header.datasize,  image[0].image[0].matrix.datasize);
     return (FALSE);
   }
Index: /branches/pap/Ohana/src/kapa2/src/PSFrame.c
===================================================================
--- /branches/pap/Ohana/src/kapa2/src/PSFrame.c	(revision 28483)
+++ /branches/pap/Ohana/src/kapa2/src/PSFrame.c	(revision 28484)
@@ -4,5 +4,5 @@
 int PSFrame (KapaGraphWidget *graph, FILE *f) {
   
-  int i, Nticks, P;
+  int i, j, Nticks, P;
   double fx, fy, dfx, dfy, lweight;
   Graphic *graphic;
@@ -34,6 +34,6 @@
     if (graph[0].axis[i].areticks) {
       ticks = CreateAxisTicks (&graph[0].axis[i], &Nticks);
-      for (i = 0; i < Nticks; i++) {
-	PSTick (graphic, &graph[0].axis[i], P, &ticks[i], i, f);
+      for (j = 0; j < Nticks; j++) {
+	PSTick (graphic, &graph[0].axis[i], P, &ticks[j], i, f);
       }
       FREE (ticks);
@@ -72,5 +72,5 @@
   y = fy + (value-min)*dfy/(max - min);
 
-  dir = ((naxis == 0) || (naxis == 1)) ? -1 : +1;
+  dir = ((naxis == 0) || (naxis == 1)) ? +1 : -1;
   dx = dir*size*dfy*n;	
   dy = dir*size*dfx*n;
@@ -86,6 +86,6 @@
     
     /* temporarily assume rectilinear axes */
-    if (naxis == 0) { dx = 0; dy = +pad; pos = 1; }
-    if (naxis == 2) { dx = 0; dy = -pad; pos = 7; }
+    if (naxis == 0) { dx = 0; dy = -pad; pos = 1; }
+    if (naxis == 2) { dx = 0; dy = +pad; pos = 7; }
 
     if (naxis == 1) { dy = 0; dx = -pad; pos = 3; }
Index: /branches/pap/Ohana/src/kapa2/src/bDrawFrame.c
===================================================================
--- /branches/pap/Ohana/src/kapa2/src/bDrawFrame.c	(revision 28483)
+++ /branches/pap/Ohana/src/kapa2/src/bDrawFrame.c	(revision 28484)
@@ -4,5 +4,5 @@
 int bDrawFrame (KapaGraphWidget *graph) {
   
-  int i, Nticks, P;
+  int i, j, Nticks, P;
   double fx, fy, dfx, dfy, lweight;
   // Graphic graphic; is not needed
@@ -33,6 +33,6 @@
     if (graph[0].axis[i].areticks) {
       ticks = CreateAxisTicks (&graph[0].axis[i], &Nticks);
-      for (i = 0; i < Nticks; i++) {
-	bDrawTick (&graph[0].axis[i], P, &ticks[i], i);
+      for (j = 0; j < Nticks; j++) {
+	bDrawTick (&graph[0].axis[i], P, &ticks[j], i);
       }
       FREE (ticks);
Index: /branches/pap/Ohana/src/libautocode/def/autocode.c
===================================================================
--- /branches/pap/Ohana/src/libautocode/def/autocode.c	(revision 28483)
+++ /branches/pap/Ohana/src/libautocode/def/autocode.c	(revision 28484)
@@ -7,5 +7,5 @@
 
   if (size != $SIZE) { 
-    fprintf (stderr, "WARNING: mismatch in data types $STRUCT: %lld vs %lld\n", (long long) size, (long long) $SIZE);
+    fprintf (stderr, "WARNING: mismatch in data types $STRUCT: "OFF_T_FMT" vs %d\n",  size,  $SIZE);
     return (FALSE);
   }
@@ -32,5 +32,5 @@
 
   if ($SIZE > size) { 
-    fprintf (stderr, "ERROR: uncorrectable mismatch in data types $STRUCT: %lld vs %lld\n", (long long) size, (long long) $SIZE);
+    fprintf (stderr, "ERROR: uncorrectable mismatch in data types $STRUCT: "OFF_T_FMT" vs %d\n",  size,  $SIZE);
     exit (1);
   }
@@ -60,5 +60,12 @@
 $STRUCT *gfits_table_get_$STRUCT (FTable *ftable, off_t *Ndata, char *swapped) {
 
+  int Ncols;
   $STRUCT *data, *output;
+
+  Ncols = ftable[0].header[0].Naxis[0];
+  if (Ncols != $SIZE) {
+    fprintf (stderr, "ERROR: mis-match in table size: width is %d but should be %d bytes\n", Ncols, $SIZE);
+    exit (1);
+  }
 
   *Ndata = ftable[0].header[0].Naxis[1];
@@ -84,5 +91,5 @@
 
   /* create table header */
-  gfits_create_table_header (header, "$TYPE", "$EXTNAME");
+  if (!gfits_create_table_header (header, "$TYPE", "$EXTNAME")) return (FALSE);
 
   /* define table layout */
@@ -90,10 +97,10 @@
 
   /* create table */
-  gfits_create_table (header, ftable);
+  if (!gfits_create_table (header, ftable)) return (FALSE);
 
   /* add data values */
-  gfits_table_scale_data (ftable);
-  gfits_convert_$STRUCT (data, sizeof ($STRUCT), Ndata);
-  gfits_add_rows (ftable, (char *) data, Ndata, sizeof ($STRUCT));
+  if (!gfits_table_scale_data (ftable)) return (FALSE);
+  if (!gfits_convert_$STRUCT (data, sizeof ($STRUCT), Ndata)) return (FALSE);
+  if (!gfits_add_rows (ftable, (char *) data, Ndata, sizeof ($STRUCT))) return (FALSE);
 
   return (TRUE);
@@ -103,5 +110,5 @@
 
   /* create table header */
-  gfits_create_table_header (header, "$TYPE", "$EXTNAME");
+  if (!gfits_create_table_header (header, "$TYPE", "$EXTNAME")) return (FALSE);
 
   /* define table layout */
@@ -125,5 +132,5 @@
   }
 
-  gfits_convert_$STRUCT (tmpdata, sizeof ($STRUCT), Ndata);
+  if (!gfits_convert_$STRUCT (tmpdata, sizeof ($STRUCT), Ndata)) return (FALSE);
 
   SendCommand (device, 16, "NVALUE: %6d", Ndata);
@@ -153,5 +160,5 @@
   
   tmpdata = ($STRUCT *) message.buffer;
-  gfits_convert_$STRUCT (tmpdata, sizeof ($STRUCT), ndata);
+  if (!gfits_convert_$STRUCT (tmpdata, sizeof ($STRUCT), ndata)) return (FALSE);
 
   /* double-check data length? */
Index: /branches/pap/Ohana/src/libautocode/def/common.h
===================================================================
--- /branches/pap/Ohana/src/libautocode/def/common.h	(revision 28483)
+++ /branches/pap/Ohana/src/libautocode/def/common.h	(revision 28484)
@@ -22,18 +22,8 @@
   tmp = byte[X+3]; byte[X+3] = byte[X+4]; byte[X+4] = tmp;
 
-# ifdef linux
-# define BYTE_SWAP
+# ifndef BYTE_SWAP
+# ifndef NOT_BYTE_SWAP
+# error "neither BYTE_SWAP not NOT_BYTE_SWAP is set"
 # endif
-
-# ifdef darwin_x86
-# define BYTE_SWAP
-# endif
-
-# ifdef sid
-# define BYTE_SWAP
-# endif
-
-# ifdef dec
-# define BYTE_SWAP
 # endif
 
Index: /branches/pap/Ohana/src/libdvo/src/LoadPhotcodes.c
===================================================================
--- /branches/pap/Ohana/src/libdvo/src/LoadPhotcodes.c	(revision 28483)
+++ /branches/pap/Ohana/src/libdvo/src/LoadPhotcodes.c	(revision 28484)
@@ -15,5 +15,5 @@
   if (LoadPhotcodesText (master_file)) { 
     if (!check_file_access (catdir_file, TRUE, TRUE, TRUE)) return TRUE;
-    SavePhotcodesFITS (catdir_file);
+    if (!SavePhotcodesFITS (catdir_file)) return FALSE;
     return TRUE;
   }
Index: /branches/pap/Ohana/src/libdvo/src/SavePhotcodesFITS.c
===================================================================
--- /branches/pap/Ohana/src/libdvo/src/SavePhotcodesFITS.c	(revision 28483)
+++ /branches/pap/Ohana/src/libdvo/src/SavePhotcodesFITS.c	(revision 28484)
@@ -32,8 +32,8 @@
 
   /* convert FITS format data to internal format (byteswaps & EXTNAME) */
-  gfits_db_create (&db);
-  gfits_table_set_PhotCode_PS1_V2 (&db.ftable, photcode_output, table[0].Ncode);
-  gfits_db_save (&db);
-  gfits_db_close (&db);
+  if (!gfits_db_create (&db)) return (FALSE);
+  if (!gfits_table_set_PhotCode_PS1_V2 (&db.ftable, photcode_output, table[0].Ncode)) return (FALSE);
+  if (!gfits_db_save (&db)) return (FALSE);
+  if (!gfits_db_close (&db)) return (FALSE);
 
   free (photcode_output);
Index: /branches/pap/Ohana/src/libdvo/src/SavePhotcodesText.c
===================================================================
--- /branches/pap/Ohana/src/libdvo/src/SavePhotcodesText.c	(revision 28483)
+++ /branches/pap/Ohana/src/libdvo/src/SavePhotcodesText.c	(revision 28484)
@@ -69,5 +69,5 @@
 	     type,
 	     table[0].code[i].C*SCALE, 
-	     table[0].code[i].K*SCALE, 
+	     table[0].code[i].K, 
 	     table[0].code[i].dC*SCALE);
 
Index: /branches/pap/Ohana/src/libdvo/src/cmf-ps1-v1-alt.c
===================================================================
--- /branches/pap/Ohana/src/libdvo/src/cmf-ps1-v1-alt.c	(revision 28483)
+++ /branches/pap/Ohana/src/libdvo/src/cmf-ps1-v1-alt.c	(revision 28484)
@@ -13,5 +13,5 @@
   // this function is a special case : it must have Nx = 136
   if (ftable[0].header[0].Naxis[0] != 136) { 
-    fprintf (stderr, "ERROR: wrong format for CMF_PS1_V1_Alt: %lld vs %d\n", (long long) ftable[0].header[0].Naxis[0], 136);
+    fprintf (stderr, "ERROR: wrong format for CMF_PS1_V1_Alt: "OFF_T_FMT" vs %d\n",  ftable[0].header[0].Naxis[0], 136);
     exit (2);
   }
Index: /branches/pap/Ohana/src/libdvo/src/dvo_catalog_mef.c
===================================================================
--- /branches/pap/Ohana/src/libdvo/src/dvo_catalog_mef.c	(revision 28483)
+++ /branches/pap/Ohana/src/libdvo/src/dvo_catalog_mef.c	(revision 28484)
@@ -24,7 +24,7 @@
   }
   /* get the components from the header */
-  if (!gfits_scan (&catalog[0].header, "NSTARS",   "%lld", 1, (long long *) &Naverage)) return (FALSE);
-  if (!gfits_scan (&catalog[0].header, "NMEAS",    "%lld", 1, (long long *) &Nmeasure)) return (FALSE);
-  if (!gfits_scan (&catalog[0].header, "NMISS",    "%lld", 1, (long long *) &Nmissing)) return (FALSE);
+  if (!gfits_scan (&catalog[0].header, "NSTARS",   OFF_T_FMT, 1,  &Naverage)) return (FALSE);
+  if (!gfits_scan (&catalog[0].header, "NMEAS",    OFF_T_FMT, 1,  &Nmeasure)) return (FALSE);
+  if (!gfits_scan (&catalog[0].header, "NMISS",    OFF_T_FMT, 1,  &Nmissing)) return (FALSE);
   if (!gfits_scan (&catalog[0].header, "NSECFILT", "%d",   1,               &Nsecfilt)) Nsecfilt = 0;
 
@@ -73,5 +73,5 @@
     catalog[0].average = FtableToAverage (&ftable, &Naverage, &catalog[0].catformat, &primary);
     if (Naverage != catalog[0].Naves_disk) {
-      fprintf (stderr, "Warning: mismatch between Naverage in PHU and Table headers (%lld vs %lld)\n", (long long) Naverage, (long long) catalog[0].Naves_disk);
+      fprintf (stderr, "Warning: mismatch between Naverage in PHU and Table headers ("OFF_T_FMT" vs "OFF_T_FMT")\n",  Naverage,  catalog[0].Naves_disk);
     }
     catalog[0].Naverage = catalog[0].Naves_disk;
@@ -100,5 +100,5 @@
     catalog[0].measure = FtableToMeasure (&ftable, &catalog[0].Nmeasure, &catalog[0].catformat);
     if (Nmeasure != catalog[0].Nmeas_disk) {
-      fprintf (stderr, "Warning: mismatch between Nmeasure in PHU and Table headers (%lld vs %lld)\n", (long long) Nmeasure, (long long) catalog[0].Nmeas_disk);
+      fprintf (stderr, "Warning: mismatch between Nmeasure in PHU and Table headers ("OFF_T_FMT" vs "OFF_T_FMT")\n",  Nmeasure,  catalog[0].Nmeas_disk);
     }
     catalog[0].Nmeasure = catalog[0].Nmeas_disk;
@@ -126,5 +126,5 @@
     catalog[0].missing = gfits_table_get_Missing (&ftable, &catalog[0].Nmissing, NULL);
     if (Nmissing != catalog[0].Nmiss_disk) {
-      fprintf (stderr, "Warning: mismatch between Nmissing in PHU and Table headers (%lld vs %lld)\n", (long long) Nmissing, (long long) catalog[0].Nmiss_disk);
+      fprintf (stderr, "Warning: mismatch between Nmissing in PHU and Table headers ("OFF_T_FMT" vs "OFF_T_FMT")\n",  Nmissing,  catalog[0].Nmiss_disk);
     }
     catalog[0].Nmissing = catalog[0].Nmiss_disk;
@@ -154,5 +154,5 @@
     catalog[0].secfilt = FtableToSecFilt (&ftable, &Nitems, &catalog[0].catformat);
     if (Nexpect != Nitems) {
-      fprintf (stderr, "Warning: mismatch between Nsecfilt items in PHU and Table headers (%lld vs %lld)\n", (long long) Nexpect, (long long) Nitems);
+      fprintf (stderr, "Warning: mismatch between Nsecfilt items in PHU and Table headers ("OFF_T_FMT" vs "OFF_T_FMT")\n",  Nexpect,  Nitems);
     }
 
@@ -238,7 +238,7 @@
 
   /* make sure header is consistent with data */
-  gfits_modify (&catalog[0].header, "NSTARS",   "%lld", 1, (long long) catalog[0].Naverage);
-  gfits_modify (&catalog[0].header, "NMEAS",    "%lld", 1, (long long) catalog[0].Nmeasure);
-  gfits_modify (&catalog[0].header, "NMISS",    "%lld", 1, (long long) catalog[0].Nmissing);
+  gfits_modify (&catalog[0].header, "NSTARS",   OFF_T_FMT, 1,  catalog[0].Naverage);
+  gfits_modify (&catalog[0].header, "NMEAS",    OFF_T_FMT, 1,  catalog[0].Nmeasure);
+  gfits_modify (&catalog[0].header, "NMISS",    OFF_T_FMT, 1,  catalog[0].Nmissing);
   gfits_modify (&catalog[0].header, "NSECFILT", "%d",   1,                        Nsecfilt);
   gfits_modify_alt (&catalog[0].header, "EXTEND",   "%t", 1, TRUE);
Index: /branches/pap/Ohana/src/libdvo/src/dvo_catalog_raw.c
===================================================================
--- /branches/pap/Ohana/src/libdvo/src/dvo_catalog_raw.c	(revision 28483)
+++ /branches/pap/Ohana/src/libdvo/src/dvo_catalog_raw.c	(revision 28484)
@@ -21,7 +21,7 @@
   /* get the components from the header */
   catalog[0].Naverage = catalog[0].Nmeasure = catalog[0].Nmissing = catalog[0].Nsecfilt = 0;
-  if (!gfits_scan (&catalog[0].header, "NSTARS",   "%lld", 1, (long long *) &catalog[0].Naverage)) return (FALSE);
-  if (!gfits_scan (&catalog[0].header, "NMEAS",    "%lld", 1, (long long *) &catalog[0].Nmeasure)) return (FALSE);
-  if (!gfits_scan (&catalog[0].header, "NMISS",    "%lld", 1, (long long *) &catalog[0].Nmissing)) return (FALSE);
+  if (!gfits_scan (&catalog[0].header, "NSTARS",   OFF_T_FMT, 1,  &catalog[0].Naverage)) return (FALSE);
+  if (!gfits_scan (&catalog[0].header, "NMEAS",    OFF_T_FMT, 1,  &catalog[0].Nmeasure)) return (FALSE);
+  if (!gfits_scan (&catalog[0].header, "NMISS",    OFF_T_FMT, 1,  &catalog[0].Nmissing)) return (FALSE);
   if (!gfits_scan (&catalog[0].header, "NSECFILT", "%d",   1,               &catalog[0].Nsecfilt)) catalog[0].Nsecfilt = 0;
 
@@ -113,9 +113,9 @@
     if (VERBOSE) {
       fprintf (stderr, "star catalog has inconsistent size\n");
-      fprintf (stderr, "average: %lld = %lld bytes\n", (long long) catalog[0].Naverage, (long long) catalog[0].Naverage*AverageSize);
-      fprintf (stderr, "measure: %lld = %lld bytes\n", (long long) catalog[0].Nmeasure, (long long) catalog[0].Nmeasure*MeasureSize);
-      fprintf (stderr, "missing: %lld = %lld bytes\n", (long long) catalog[0].Nmissing, (long long) catalog[0].Nmissing*MissingSize);
-      fprintf (stderr, "secfilt: %lld = %lld bytes\n", (long long) catalog[0].Nsecfilt, (long long) catalog[0].Nsecfilt*SecFiltSize*catalog[0].Naverage);
-      fprintf (stderr, "expect: %lld, found: %lld\n", (long long) size, (long long) filestatus.st_size);
+      fprintf (stderr, "average: "OFF_T_FMT" = "OFF_T_FMT" bytes\n",  catalog[0].Naverage,  catalog[0].Naverage*AverageSize);
+      fprintf (stderr, "measure: "OFF_T_FMT" = "OFF_T_FMT" bytes\n",  catalog[0].Nmeasure,  catalog[0].Nmeasure*MeasureSize);
+      fprintf (stderr, "missing: "OFF_T_FMT" = "OFF_T_FMT" bytes\n",  catalog[0].Nmissing,  catalog[0].Nmissing*MissingSize);
+      fprintf (stderr, "secfilt: %d = "OFF_T_FMT" bytes\n",  catalog[0].Nsecfilt,  catalog[0].Nsecfilt*SecFiltSize*catalog[0].Naverage);
+      fprintf (stderr, "expect: "OFF_T_FMT", found: "OFF_T_FMT"\n",  size,  filestatus.st_size);
     }
     return (FALSE);
@@ -155,5 +155,5 @@
     nitems = fread (catalog[0].missing, MissingSize, Nitems, f);
     if (nitems != Nitems) {
-      if (VERBOSE) fprintf (stderr, "failed to read missing from catalog file %s (%lld vs %lld)\n", catalog[0].filename, (long long) nitems, (long long) Nitems);
+      if (VERBOSE) fprintf (stderr, "failed to read missing from catalog file %s ("OFF_T_FMT" vs "OFF_T_FMT")\n", catalog[0].filename,  nitems,  Nitems);
       return (FALSE);
     }
@@ -198,10 +198,10 @@
   }
 
-  if (VERBOSE) fprintf (stderr, "read %lld stars from catalog file %s (%lld measurements, %lld missing, %lld secondary filters)\n", 
-			(long long) catalog[0].Naverage, 
+  if (VERBOSE) fprintf (stderr, "read "OFF_T_FMT" stars from catalog file %s ("OFF_T_FMT" measurements, "OFF_T_FMT" missing, %d secondary filters)\n", 
+			 catalog[0].Naverage, 
 			catalog[0].filename, 
-			(long long) catalog[0].Nmeasure, 
-			(long long) catalog[0].Nmissing, 
-			(long long) catalog[0].Nsecfilt);
+			 catalog[0].Nmeasure, 
+			 catalog[0].Nmissing, 
+			 catalog[0].Nsecfilt);
 
   /* check data integrity */
@@ -214,6 +214,6 @@
       if (VERBOSE) {
 	fprintf (stderr, "****** data in catalog %s is corrupt, sums don't check\n", catalog[0].filename);
-	fprintf (stderr, "****** Nmeas: %lld, %lld\n", (long long) Nmeas, (long long) catalog[0].Nmeasure);
-	fprintf (stderr, "****** Nmiss: %lld, %lld\n", (long long) Nmiss, (long long) catalog[0].Nmissing);
+	fprintf (stderr, "****** Nmeas: "OFF_T_FMT", "OFF_T_FMT"\n",  Nmeas,  catalog[0].Nmeasure);
+	fprintf (stderr, "****** Nmiss: "OFF_T_FMT", "OFF_T_FMT"\n",  Nmiss,  catalog[0].Nmissing);
       }
       return (FALSE);
@@ -264,8 +264,8 @@
 
   /* make sure header is consistent with data */
-  gfits_modify (&catalog[0].header, "NSTARS",   "%lld", 1, (long long) catalog[0].Naverage);
-  gfits_modify (&catalog[0].header, "NMEAS",    "%lld", 1, (long long) catalog[0].Nmeasure);
-  gfits_modify (&catalog[0].header, "NMISS",    "%lld", 1, (long long) catalog[0].Nmissing);
-  gfits_modify (&catalog[0].header, "NSECFILT", "%lld", 1, (long long) catalog[0].Nsecfilt);
+  gfits_modify (&catalog[0].header, "NSTARS",   OFF_T_FMT, 1,  catalog[0].Naverage);
+  gfits_modify (&catalog[0].header, "NMEAS",    OFF_T_FMT, 1,  catalog[0].Nmeasure);
+  gfits_modify (&catalog[0].header, "NMISS",    OFF_T_FMT, 1,  catalog[0].Nmissing);
+  gfits_modify (&catalog[0].header, "NSECFILT", "%d", 1,  catalog[0].Nsecfilt);
   gfits_modify (&catalog[0].header, "OBJID",    "%d", 1, catalog[0].objID);
 
@@ -353,5 +353,5 @@
       nitems = fread (tmpAverage, sizeof(Average_##TYPE), Naverage, f); \
       if (nitems != Naverage) { \
-	fprintf (stderr, "failed to read averages (%lld vs %lld)\n", (long long) nitems, (long long) Naverage); \
+	fprintf (stderr, "failed to read averages ("OFF_T_FMT" vs "OFF_T_FMT")\n",  nitems,  Naverage); \
 	return (NULL); \
       } \
@@ -367,5 +367,5 @@
       nitems = fread (average, sizeof(Average), Naverage, f);
       if (nitems != Naverage) {
-	fprintf (stderr, "failed to read averages (%lld vs %lld)\n", (long long) nitems, (long long) Naverage);
+	fprintf (stderr, "failed to read averages ("OFF_T_FMT" vs "OFF_T_FMT")\n",  nitems,  Naverage);
 	return (NULL);
       }
@@ -406,5 +406,5 @@
       free (tmpAverage); \
       if (nitems != Naverage) { \
-	fprintf (stderr, "failed to write averages (%lld vs %lld)\n", (long long) nitems, (long long) Naverage); \
+	fprintf (stderr, "failed to write averages ("OFF_T_FMT" vs "OFF_T_FMT")\n",  nitems,  Naverage); \
 	return (FALSE); \
       } \
@@ -417,5 +417,5 @@
       nitems = fwrite (average, sizeof(Average), Naverage, f);
       if (nitems != Naverage) {
-	fprintf (stderr, "failed to write averages (%lld vs %lld)\n", (long long) nitems, (long long) Naverage);
+	fprintf (stderr, "failed to write averages ("OFF_T_FMT" vs "OFF_T_FMT")\n",  nitems,  Naverage);
 	return (FALSE);
       }
@@ -455,5 +455,5 @@
       nitems = fread (tmpMeasure, sizeof(Measure_##TYPE), Nmeasure, f); \
       if (nitems != Nmeasure) { \
-	fprintf (stderr, "failed to read measures (%lld vs %lld)\n", (long long) nitems, (long long) Nmeasure); \
+	fprintf (stderr, "failed to read measures ("OFF_T_FMT" vs "OFF_T_FMT")\n",  nitems,  Nmeasure); \
 	return (NULL); \
       } \
@@ -469,5 +469,5 @@
       nitems = fread (measure, sizeof(Measure), Nmeasure, f);
       if (nitems != Nmeasure) {
-	fprintf (stderr, "failed to read measures (%lld vs %lld)\n", (long long) nitems, (long long) Nmeasure);
+	fprintf (stderr, "failed to read measures ("OFF_T_FMT" vs "OFF_T_FMT")\n",  nitems,  Nmeasure);
 	return (NULL);
       }
@@ -508,5 +508,5 @@
       free (tmpMeasure); \
       if (nitems != Nmeasure) { \
-	fprintf (stderr, "failed to write measures (%lld vs %lld)\n", (long long) nitems, (long long) Nmeasure); \
+	fprintf (stderr, "failed to write measures ("OFF_T_FMT" vs "OFF_T_FMT")\n",  nitems,  Nmeasure); \
 	return (FALSE); \
       } \
@@ -519,5 +519,5 @@
       nitems = fwrite (measure, sizeof(Measure), Nmeasure, f);
       if (nitems != Nmeasure) {
-	fprintf (stderr, "failed to write measures (%lld vs %lld)\n", (long long) nitems, (long long) Nmeasure);
+	fprintf (stderr, "failed to write measures ("OFF_T_FMT" vs "OFF_T_FMT")\n",  nitems,  Nmeasure);
 	return (FALSE);
       }
@@ -557,5 +557,5 @@
       nitems = fread (tmpSecFilt, sizeof(SecFilt_##TYPE), Nsecfilt, f); \
       if (nitems != Nsecfilt) { \
-	fprintf (stderr, "failed to read secfilts (%lld vs %lld)\n", (long long) nitems, (long long) Nsecfilt); \
+	fprintf (stderr, "failed to read secfilts ("OFF_T_FMT" vs "OFF_T_FMT")\n",  nitems,  Nsecfilt); \
 	return (NULL); \
       } \
@@ -571,5 +571,5 @@
       nitems = fread (secfilt, sizeof(SecFilt), Nsecfilt, f);
       if (nitems != Nsecfilt) {
-	fprintf (stderr, "failed to read secfilts (%lld vs %lld)\n", (long long) nitems, (long long) Nsecfilt);
+	fprintf (stderr, "failed to read secfilts ("OFF_T_FMT" vs "OFF_T_FMT")\n",  nitems,  Nsecfilt);
 	return (NULL);
       }
@@ -610,5 +610,5 @@
       free (tmpSecFilt); \
       if (nitems != Nsecfilt) { \
-	fprintf (stderr, "failed to write secfilts (%lld vs %lld)\n", (long long) nitems, (long long) Nsecfilt); \
+	fprintf (stderr, "failed to write secfilts ("OFF_T_FMT" vs "OFF_T_FMT")\n",  nitems,  Nsecfilt); \
 	return (FALSE); \
       } \
@@ -621,5 +621,5 @@
       nitems = fwrite (secfilt, sizeof(SecFilt), Nsecfilt, f);
       if (nitems != Nsecfilt) {
-	fprintf (stderr, "failed to write secfilts (%lld vs %lld)\n", (long long) nitems, (long long) Nsecfilt);
+	fprintf (stderr, "failed to write secfilts ("OFF_T_FMT" vs "OFF_T_FMT")\n",  nitems,  Nsecfilt);
 	return (FALSE);
       }
Index: /branches/pap/Ohana/src/libdvo/src/dvo_catalog_split.c
===================================================================
--- /branches/pap/Ohana/src/libdvo/src/dvo_catalog_split.c	(revision 28483)
+++ /branches/pap/Ohana/src/libdvo/src/dvo_catalog_split.c	(revision 28484)
@@ -174,7 +174,7 @@
 
   /* get the components from the header - these duplicate information in the split files (NAXIS2) */
-  if (!gfits_scan (&catalog[0].header, "NSTARS",   "%lld", 1, (long long *) &Naverage)) return (FALSE);
-  if (!gfits_scan (&catalog[0].header, "NMEAS",    "%lld", 1, (long long *) &Nmeasure)) return (FALSE);
-  if (!gfits_scan (&catalog[0].header, "NMISS",    "%lld", 1, (long long *) &Nmissing)) return (FALSE);
+  if (!gfits_scan (&catalog[0].header, "NSTARS",   OFF_T_FMT, 1,  &Naverage)) return (FALSE);
+  if (!gfits_scan (&catalog[0].header, "NMEAS",    OFF_T_FMT, 1,  &Nmeasure)) return (FALSE);
+  if (!gfits_scan (&catalog[0].header, "NMISS",    OFF_T_FMT, 1,  &Nmissing)) return (FALSE);
   if (!gfits_scan (&catalog[0].header, "NSECFILT", "%d",   1,               &Nsecfilt)) Nsecfilt = 0;
 
@@ -227,5 +227,5 @@
     catalog[0].average = FtableToAverage (&ftable, &Naverage, &catalog[0].catformat, &primary);
     if (Naverage != catalog[0].Naves_disk) {
-      fprintf (stderr, "Warning: mismatch between Naverage in PHU and Table headers (%lld vs %lld)\n", (long long) Naverage, (long long) catalog[0].Naves_disk);
+      fprintf (stderr, "Warning: mismatch between Naverage in PHU and Table headers ("OFF_T_FMT" vs "OFF_T_FMT")\n",  Naverage,  catalog[0].Naves_disk);
     }
     gfits_free_header (&header);
@@ -256,5 +256,5 @@
     catalog[0].measure = FtableToMeasure (&ftable, &Nmeasure, &catalog[0].catformat);
     if (Nmeasure != catalog[0].Nmeas_disk) {
-      fprintf (stderr, "Warning: mismatch between Nmeasure in PHU and Table headers (%lld vs %lld)\n", (long long) Nmeasure, (long long) catalog[0].Nmeas_disk);
+      fprintf (stderr, "Warning: mismatch between Nmeasure in PHU and Table headers ("OFF_T_FMT" vs "OFF_T_FMT")\n",  Nmeasure,  catalog[0].Nmeas_disk);
     }
     catalog[0].Nmeasure = catalog[0].Nmeas_disk;
@@ -286,5 +286,5 @@
     catalog[0].missing = gfits_table_get_Missing (&ftable, &Nmissing, NULL);
     if (Nmissing != catalog[0].Nmiss_disk) {
-      fprintf (stderr, "Warning: mismatch between Nmissing in PHU and Table headers (%lld vs %lld)\n", (long long) Nmissing, (long long) catalog[0].Nmiss_disk);
+      fprintf (stderr, "Warning: mismatch between Nmissing in PHU and Table headers ("OFF_T_FMT" vs "OFF_T_FMT")\n",  Nmissing,  catalog[0].Nmiss_disk);
     }
     catalog[0].Nmissing = catalog[0].Nmiss_disk;
@@ -315,5 +315,5 @@
     catalog[0].secfilt = FtableToSecFilt (&ftable, &Nitems, &catalog[0].catformat);
     if (Nitems != catalog[0].Nsecf_disk) {
-      fprintf (stderr, "Warning: mismatch between Nsecfilt items in PHU and Table headers (%lld vs %lld)\n", (long long) Nitems, (long long) catalog[0].Nsecf_disk);
+      fprintf (stderr, "Warning: mismatch between Nsecfilt items in PHU and Table headers ("OFF_T_FMT" vs "OFF_T_FMT")\n",  Nitems,  catalog[0].Nsecf_disk);
     }
     catalog[0].Nsecf_mem = catalog[0].Nsecf_disk;
@@ -408,5 +408,5 @@
     catalog[0].secfilt = FtableToSecFilt (&ftable, &Nitems, &catalog[0].catformat);
     if (Nitems != Nexpect) {
-      fprintf (stderr, "Warning: mismatch between Nsecfilt items in PHU and Table headers (%lld vs %lld)\n", (long long) Nitems, (long long) Nexpect);
+      fprintf (stderr, "Warning: mismatch between Nsecfilt items in PHU and Table headers ("OFF_T_FMT" vs "OFF_T_FMT")\n",  Nitems,  Nexpect);
     }
     catalog[0].Nsecf_mem = catalog[0].Naverage * catalog[0].Nsecfilt;
@@ -444,5 +444,5 @@
     catalog[0].measure = FtableToMeasure (&ftable, &Nmeasure, &catalog[0].catformat);
     if (Nmeasure != Nrows) {
-      fprintf (stderr, "Warning: mismatch between Nmeasure in PHU and Table headers (%lld vs %lld)\n", (long long) Nmeasure, (long long) Nrows);
+      fprintf (stderr, "Warning: mismatch between Nmeasure in PHU and Table headers ("OFF_T_FMT" vs "OFF_T_FMT")\n",  Nmeasure,  Nrows);
     }
     gfits_free_header (&header);
@@ -474,5 +474,5 @@
     catalog[0].missing = gfits_table_get_Missing (&ftable, &Nmissing, NULL);
     if (Nmissing != Nrows) {
-      fprintf (stderr, "Warning: mismatch between Nmissing in PHU and Table headers (%lld vs %lld)\n", (long long) Nmissing, (long long) Nrows);
+      fprintf (stderr, "Warning: mismatch between Nmissing in PHU and Table headers ("OFF_T_FMT" vs "OFF_T_FMT")\n",  Nmissing,  Nrows);
     }
     gfits_free_header (&header);
@@ -524,7 +524,7 @@
 
   /* make sure header is consistent with data */
-  gfits_modify (&catalog[0].header, "NSTARS",   "%lld", 1, (long long) Naves_disk_new);
-  gfits_modify (&catalog[0].header, "NMEAS",    "%lld", 1, (long long) Nmeas_disk_new);
-  gfits_modify (&catalog[0].header, "NMISS",    "%lld", 1, (long long) Nmiss_disk_new);
+  gfits_modify (&catalog[0].header, "NSTARS",   OFF_T_FMT, 1,  Naves_disk_new);
+  gfits_modify (&catalog[0].header, "NMEAS",    OFF_T_FMT, 1,  Nmeas_disk_new);
+  gfits_modify (&catalog[0].header, "NMISS",    OFF_T_FMT, 1,  Nmiss_disk_new);
   gfits_modify (&catalog[0].header, "NSECFILT", "%d",   1, Nsecfilt);
   gfits_modify_alt (&catalog[0].header, "EXTEND",   "%t", 1, TRUE);
@@ -723,7 +723,7 @@
 
   /* make sure header is consistent with data */
-  gfits_modify (&catalog[0].header, "NSTARS",   "%lld", 1, (long long) Naves_disk_new);
-  gfits_modify (&catalog[0].header, "NMEAS",    "%lld", 1, (long long) Nmeas_disk_new);
-  gfits_modify (&catalog[0].header, "NMISS",    "%lld", 1, (long long) Nmiss_disk_new);
+  gfits_modify (&catalog[0].header, "NSTARS",   OFF_T_FMT, 1,  Naves_disk_new);
+  gfits_modify (&catalog[0].header, "NMEAS",    OFF_T_FMT, 1,  Nmeas_disk_new);
+  gfits_modify (&catalog[0].header, "NMISS",    OFF_T_FMT, 1,  Nmiss_disk_new);
   gfits_modify (&catalog[0].header, "NSECFILT", "%d", 1, Nsecfilt);
   gfits_modify_alt (&catalog[0].header, "EXTEND",   "%t", 1, TRUE);
@@ -902,7 +902,7 @@
 
   /* make sure header is consistent with data */
-  gfits_modify (&catalog[0].header, "NSTARS",   "%lld", 1, (long long) Naves_disk_new);
-  gfits_modify (&catalog[0].header, "NMEAS",    "%lld", 1, (long long) Nmeas_disk_new);
-  gfits_modify (&catalog[0].header, "NMISS",    "%lld", 1, (long long) Nmiss_disk_new);
+  gfits_modify (&catalog[0].header, "NSTARS",   OFF_T_FMT, 1,  Naves_disk_new);
+  gfits_modify (&catalog[0].header, "NMEAS",    OFF_T_FMT, 1,  Nmeas_disk_new);
+  gfits_modify (&catalog[0].header, "NMISS",    OFF_T_FMT, 1,  Nmiss_disk_new);
   gfits_modify (&catalog[0].header, "NSECFILT", "%d", 1, Nsecfilt);
   gfits_modify_alt (&catalog[0].header, "EXTEND",   "%t", 1, TRUE);
Index: /branches/pap/Ohana/src/libdvo/src/dvo_convert.c
===================================================================
--- /branches/pap/Ohana/src/libdvo/src/dvo_convert.c	(revision 28483)
+++ /branches/pap/Ohana/src/libdvo/src/dvo_convert.c	(revision 28484)
@@ -267,5 +267,5 @@
     gfits_free_header (theader);
     gfits_table_mkheader_Image (theader);
-    gfits_modify (theader, "NAXIS2", "%lld", 1, (long long) Nimage);
+    gfits_modify (theader, "NAXIS2", OFF_T_FMT, 1,  Nimage);
     theader[0].Naxis[1] = Nimage;
     ftable[0].datasize = gfits_data_size (theader);
@@ -288,5 +288,5 @@
     gfits_free_header (theader); \
     gfits_table_mkheader_Image (theader); \
-    gfits_modify (theader, "NAXIS2", "%lld", 1, (long long) Nimage); \
+    gfits_modify (theader, "NAXIS2", OFF_T_FMT, 1,  Nimage); \
     theader[0].Naxis[1] = Nimage; \
     ftable[0].datasize = gfits_data_size (theader); \
@@ -373,8 +373,8 @@
       } \
       /* convert header from old format to new format */ \
-      gfits_scan (theader, "NAXIS2", "%lld", 1, (long long *) &Nimage); \
+      gfits_scan (theader, "NAXIS2", OFF_T_FMT, 1,  &Nimage); \
       gfits_free_header (theader); \
       gfits_table_mkheader_Image_##TYPE (theader); \
-      gfits_modify (theader, "NAXIS2", "%lld", 1, (long long) Nimage); \
+      gfits_modify (theader, "NAXIS2", OFF_T_FMT, 1,  Nimage); \
       theader[0].Naxis[1] = Nimage; \
       vtable[0].datasize = gfits_data_size (theader); \
Index: /branches/pap/Ohana/src/libdvo/src/dvo_image.c
===================================================================
--- /branches/pap/Ohana/src/libdvo/src/dvo_image.c	(revision 28483)
+++ /branches/pap/Ohana/src/libdvo/src/dvo_image.c	(revision 28484)
@@ -120,14 +120,17 @@
   /* adjust header */
   Nimages = 0;
-  gfits_scan (&db[0].header, "NIMAGES", "%lld", 1, (long long *) &Nimages);
+  if (!gfits_scan (&db[0].header, "NIMAGES", OFF_T_FMT, 1,  &Nimages)) return (FALSE);
   Nimages += Nnew;
-  gfits_modify (&db[0].header, "NIMAGES", "%lld", 1, (long long) Nimages);
-
-  gfits_table_to_vtable (&db[0].ftable, &db[0].vtable, 0, 0);
-  gfits_vadd_rows (&db[0].vtable, (char *) new, Nnew, sizeof(Image));
+
+  if (!gfits_modify (&db[0].header, "NIMAGES", OFF_T_FMT, 1,  Nimages)) return (FALSE);
+
+  if (!gfits_table_to_vtable (&db[0].ftable, &db[0].vtable, 0, 0)) return (FALSE);
+
+  if (!gfits_vadd_rows (&db[0].vtable, (char *) new, Nnew, sizeof(Image))) return (FALSE);
 
   /* check that primary header and table header agree */
   if (Nimages != db[0].theader.Naxis[1]) {
     fprintf (stderr, "header / table length mismatch!\n");
+    return (FALSE);
   }
   return (TRUE);
@@ -157,5 +160,5 @@
       exit (2);
   }
-  return (TRUE);
+  return (status);
 }
 
@@ -195,5 +198,5 @@
   gfits_table_set_Image (&db[0].ftable, NULL, 0);
 
-  gfits_modify (&db[0].header, "NIMAGES", "%lld", 1, 0LL);
+  gfits_modify (&db[0].header, "NIMAGES", "%d", 1, 0);
   gfits_modify (&db[0].header, "ZERO_PT", "%lf", 1, ZeroPoint);
 
Index: /branches/pap/Ohana/src/libdvo/src/dvo_image_raw.c
===================================================================
--- /branches/pap/Ohana/src/libdvo/src/dvo_image_raw.c	(revision 28483)
+++ /branches/pap/Ohana/src/libdvo/src/dvo_image_raw.c	(revision 28484)
@@ -39,5 +39,5 @@
   /* find number of images */
   Nimage = 0;
-  gfits_scan (&db[0].header, "NIMAGES", "%lld", 1, (long long *) &Nimage);
+  gfits_scan (&db[0].header, "NIMAGES", OFF_T_FMT, 1,  &Nimage);
   if (stat (db[0].filename, &filestatus) == -1) {
     if (VERBOSE) fprintf (stderr, "ERROR: failed to get status of image catalog\n");
@@ -64,5 +64,5 @@
     Ndata = (filestatus.st_size - db[0].header.datasize) / ImageSize;
     if (VERBOSE) fprintf (stderr, "ERROR: image catalog has inconsistent size\n");
-    if (VERBOSE) fprintf (stderr, "header: %lld, data: %lld\n", (long long) Nimage, (long long) Ndata);
+    if (VERBOSE) fprintf (stderr, "header: "OFF_T_FMT", data: "OFF_T_FMT"\n",  Nimage,  Ndata);
     if (!FORCE_READ) exit (1);
     Nimage = Ndata;
@@ -96,5 +96,5 @@
   } 
 
-  gfits_modify (&db[0].theader, "NAXIS2", "%lld", 1, (long long) Nimage);
+  gfits_modify (&db[0].theader, "NAXIS2", OFF_T_FMT, 1,  Nimage);
   db[0].theader.Naxis[1] = Nimage;
   db[0].ftable.datasize = gfits_data_size (&db[0].theader);
@@ -110,5 +110,5 @@
   off_t *row;
 
-  if (VERBOSE) fprintf (stderr, "writing out %lld images\n", (long long) db[0].vtable.Nrow);
+  if (VERBOSE) fprintf (stderr, "writing out "OFF_T_FMT" images\n",  db[0].vtable.Nrow);
 
   /* position to start of file */
@@ -125,6 +125,6 @@
   Nrow = db[0].vtable.Nrow;
   row = db[0].vtable.row;
-  gfits_scan (db[0].vtable.header, "NAXIS1", "%lld", 1, (long long *) &Nx);
-  gfits_scan (db[0].vtable.header, "NAXIS2", "%lld", 1, (long long *) &Ny);
+  gfits_scan (db[0].vtable.header, "NAXIS1", OFF_T_FMT, 1,  &Nx);
+  gfits_scan (db[0].vtable.header, "NAXIS2", OFF_T_FMT, 1,  &Ny);
 
   /* file pointer is at beginning of desired table data */
@@ -146,5 +146,5 @@
   int status;
 
-  if (VERBOSE) fprintf (stderr, "writing out %lld images\n", (long long) db[0].theader.Naxis[1]);
+  if (VERBOSE) fprintf (stderr, "writing out "OFF_T_FMT" images\n",  db[0].theader.Naxis[1]);
 
   /* position to start of file */
@@ -158,6 +158,6 @@
   }
 
-  gfits_scan (db[0].ftable.header, "NAXIS1", "%lld", 1, (long long *) &Nx);
-  gfits_scan (db[0].ftable.header, "NAXIS2", "%lld", 1, (long long *) &Ny);
+  gfits_scan (db[0].ftable.header, "NAXIS1", OFF_T_FMT, 1,  &Nx);
+  gfits_scan (db[0].ftable.header, "NAXIS2", OFF_T_FMT, 1,  &Ny);
   size = Nx * Ny;
   Nbytes = fwrite (db[0].ftable.buffer, sizeof(char), size, db[0].f);
Index: /branches/pap/Ohana/src/libdvo/src/fits_db.c
===================================================================
--- /branches/pap/Ohana/src/libdvo/src/fits_db.c	(revision 28483)
+++ /branches/pap/Ohana/src/libdvo/src/fits_db.c	(revision 28484)
@@ -20,9 +20,9 @@
 /* create an empty db */
 int gfits_db_create (FITS_DB *db) {
-  gfits_init_header (&db[0].header);    
+  if (!gfits_init_header (&db[0].header)) return (FALSE);
   db[0].header.extend = TRUE;
-  gfits_create_header (&db[0].header);
-  gfits_create_matrix (&db[0].header, &db[0].matrix);
-  gfits_print (&db[0].header, "NEXTEND", "%d", 1, 1);
+  if (!gfits_create_header (&db[0].header))  return (FALSE);
+  if (!gfits_create_matrix (&db[0].header, &db[0].matrix)) return (FALSE);
+  if (!gfits_print (&db[0].header, "NEXTEND", "%d", 1, 1)) return (FALSE);
   db[0].ftable.header = &db[0].theader;
   return (TRUE);
Index: /branches/pap/Ohana/src/libfits/header/F_create_H.c
===================================================================
--- /branches/pap/Ohana/src/libfits/header/F_create_H.c	(revision 28483)
+++ /branches/pap/Ohana/src/libfits/header/F_create_H.c	(revision 28484)
@@ -23,5 +23,5 @@
   for (i = 0; i < header[0].Naxes; i++) {
     snprintf (axis, 10, "NAXIS%d", i + 1);
-    gfits_modify (header,  axis, "%lld", 1, (long long) header[0].Naxis[i]);
+    gfits_modify (header,  axis, OFF_T_FMT, 1,  header[0].Naxis[i]);
   }
 
Index: /branches/pap/Ohana/src/libfits/header/F_modify.c
===================================================================
--- /branches/pap/Ohana/src/libfits/header/F_modify.c	(revision 28483)
+++ /branches/pap/Ohana/src/libfits/header/F_modify.c	(revision 28484)
@@ -51,19 +51,20 @@
 
   /* write the numeric modes */
-  if (!strcmp (mode, "%d"))   snprintf (string, 81, "%-8s= %20d / %-s ",    field, va_arg (argp, int),       	      comment);
-  if (!strcmp (mode, "%ld"))  snprintf (string, 81, "%-8s= %20ld / %-s ",   field, va_arg (argp, long),      	      comment);
-  if (!strcmp (mode, "%lld")) snprintf (string, 81, "%-8s= %20lld / %-s ",  field, va_arg (argp, long long), 	      comment);
-  if (!strcmp (mode, "%Ld"))  snprintf (string, 81, "%-8s= %20lld / %-s ",  field, va_arg (argp, long long), 	      comment);
-  if (!strcmp (mode, "%u"))   snprintf (string, 81, "%-8s= %20u / %-s ",    field, va_arg (argp, unsigned),  	      comment);
-  if (!strcmp (mode, "%lu"))  snprintf (string, 81, "%-8s= %20lu / %-s ",   field, va_arg (argp, unsigned long),      comment);
-  if (!strcmp (mode, "%llu")) snprintf (string, 81, "%-8s= %20llu / %-s ",  field, va_arg (argp, unsigned long long), comment);
-  if (!strcmp (mode, "%Lu"))  snprintf (string, 81, "%-8s= %20llu / %-s ",  field, va_arg (argp, unsigned long long), comment);
-  if (!strcmp (mode, "%hd"))  snprintf (string, 81, "%-8s= %20d / %-s ",    field, va_arg (argp, int),      	      comment); 
-  if (!strcmp (mode, "%f"))   snprintf (string, 81, "%-8s= %20.10f / %-s ", field, va_arg (argp, double),   	      comment); 
-  if (!strcmp (mode, "%lf"))  snprintf (string, 81, "%-8s= %20.10f / %-s ", field, va_arg (argp, double),   	      comment); 
-  if (!strcmp (mode, "%e"))   snprintf (string, 81, "%-8s= %20.10E / %-s ", field, va_arg (argp, double),   	      comment); 
-  if (!strcmp (mode, "%le"))  snprintf (string, 81, "%-8s= %20.10E / %-s ", field, va_arg (argp, double),   	      comment); 
-  if (!strcmp (mode, "%g"))   snprintf (string, 81, "%-8s= %20.10G / %-s ", field, va_arg (argp, double),   	      comment); 
-  if (!strcmp (mode, "%lg"))  snprintf (string, 81, "%-8s= %20.10G / %-s ", field, va_arg (argp, double),   	      comment); 
+  if (!strcmp (mode, "%d"))   { snprintf (string, 81, "%-8s= %20d / %-s ",    field, va_arg (argp, int),       	      	comment); goto found_it; }
+  if (!strcmp (mode, "%ld"))  { snprintf (string, 81, "%-8s= %20ld / %-s ",   field, va_arg (argp, long),      	      	comment); goto found_it; }
+  if (!strcmp (mode, "%lld")) { snprintf (string, 81, "%-8s= %20lld / %-s ",  field, va_arg (argp, long long), 	      	comment); goto found_it; }
+  if (!strcmp (mode, "%Ld"))  { snprintf (string, 81, "%-8s= %20lld / %-s ",  field, va_arg (argp, long long), 	      	comment); goto found_it; }
+  if (!strcmp (mode, "%u"))   { snprintf (string, 81, "%-8s= %20u / %-s ",    field, va_arg (argp, unsigned),  	      	comment); goto found_it; }
+  if (!strcmp (mode, "%lu"))  { snprintf (string, 81, "%-8s= %20lu / %-s ",   field, va_arg (argp, unsigned long),      comment); goto found_it; }
+  if (!strcmp (mode, "%llu")) { snprintf (string, 81, "%-8s= %20llu / %-s ",  field, va_arg (argp, unsigned long long), comment); goto found_it; }
+  if (!strcmp (mode, "%Lu"))  { snprintf (string, 81, "%-8s= %20llu / %-s ",  field, va_arg (argp, unsigned long long), comment); goto found_it; }
+  if (!strcmp (mode, "%hd"))  { snprintf (string, 81, "%-8s= %20d / %-s ",    field, va_arg (argp, int),      	        comment); goto found_it; } 
+  if (!strcmp (mode, "%f"))   { snprintf (string, 81, "%-8s= %20.10f / %-s ", field, va_arg (argp, double),   	        comment); goto found_it; } 
+  if (!strcmp (mode, "%lf"))  { snprintf (string, 81, "%-8s= %20.10f / %-s ", field, va_arg (argp, double),   	        comment); goto found_it; } 
+  if (!strcmp (mode, "%e"))   { snprintf (string, 81, "%-8s= %20.10E / %-s ", field, va_arg (argp, double),   	        comment); goto found_it; } 
+  if (!strcmp (mode, "%le"))  { snprintf (string, 81, "%-8s= %20.10E / %-s ", field, va_arg (argp, double),   	        comment); goto found_it; } 
+  if (!strcmp (mode, "%g"))   { snprintf (string, 81, "%-8s= %20.10G / %-s ", field, va_arg (argp, double),   	        comment); goto found_it; } 
+  if (!strcmp (mode, "%lg"))  { snprintf (string, 81, "%-8s= %20.10G / %-s ", field, va_arg (argp, double),   	        comment); goto found_it; } 
+  if (!strcmp (mode, "%jd"))  { snprintf (string, 81, "%-8s= %20jd / %-s ",   field, va_arg (argp, intmax_t), 	        comment); goto found_it; }
 
   /* string value.  Quotes must be at least 18 chars apart */
@@ -71,6 +72,11 @@
     strncpy (data, va_arg (argp, char *), 68);
     snprintf (string, 81, "%-8s= '%-18s' / %-s ", field, data, comment);
-  }
-
+    goto found_it;
+  }
+
+  /* failed to find mode */
+  return (FALSE);
+
+found_it:
   strncpy (p, string, 80);
   va_end (argp);
Index: /branches/pap/Ohana/src/libfits/header/F_print.c
===================================================================
--- /branches/pap/Ohana/src/libfits/header/F_print.c	(revision 28483)
+++ /branches/pap/Ohana/src/libfits/header/F_print.c	(revision 28484)
@@ -46,19 +46,20 @@
 
   /* write the numeric modes */
-  if (!strcmp (mode, "%d"))  { snprintf (string, 81, "%-8s= %20d / %46s ",    field, va_arg (argp, int),                blank); }
-  if (!strcmp (mode, "%ld")) { snprintf (string, 81, "%-8s= %20ld / %46s ",   field, va_arg (argp, long),               blank); }
-  if (!strcmp (mode, "%lld")){ snprintf (string, 81, "%-8s= %20lld / %46s ",  field, va_arg (argp, long long),          blank); }
-  if (!strcmp (mode, "%Ld")) { snprintf (string, 81, "%-8s= %20lld / %46s ",  field, va_arg (argp, long long),          blank); }
-  if (!strcmp (mode, "%u"))  { snprintf (string, 81, "%-8s= %20u / %46s ",    field, va_arg (argp, unsigned),           blank); }
-  if (!strcmp (mode, "%lu")) { snprintf (string, 81, "%-8s= %20lu / %46s ",   field, va_arg (argp, unsigned long),      blank); }
-  if (!strcmp (mode, "%llu")){ snprintf (string, 81, "%-8s= %20llu / %46s ",  field, va_arg (argp, unsigned long long), blank); }
-  if (!strcmp (mode, "%Lu")) { snprintf (string, 81, "%-8s= %20llu / %46s ",  field, va_arg (argp, unsigned long long), blank); }
-  if (!strcmp (mode, "%hd")) { snprintf (string, 81, "%-8s= %20d / %46s ",    field, va_arg (argp, int),                blank); }
-  if (!strcmp (mode, "%f"))  { snprintf (string, 81, "%-8s= %20.10f / %46s ", field, va_arg (argp, double),             blank); }
-  if (!strcmp (mode, "%lf")) { snprintf (string, 81, "%-8s= %20.10f / %46s ", field, va_arg (argp, double),             blank); }
-  if (!strcmp (mode, "%e"))  { snprintf (string, 81, "%-8s= %20.10E / %46s ", field, va_arg (argp, double),             blank); }
-  if (!strcmp (mode, "%le")) { snprintf (string, 81, "%-8s= %20.10E / %46s ", field, va_arg (argp, double),             blank); }
-  if (!strcmp (mode, "%g"))  { snprintf (string, 81, "%-8s= %20.10G / %46s ", field, va_arg (argp, double),             blank); }
-  if (!strcmp (mode, "%lg")) { snprintf (string, 81, "%-8s= %20.10G / %46s ", field, va_arg (argp, double),             blank); }
+  if (!strcmp (mode, "%d"))  { snprintf (string, 81, "%-8s= %20d / %46s ",    field, va_arg (argp, int),                blank); goto found_it; }
+  if (!strcmp (mode, "%ld")) { snprintf (string, 81, "%-8s= %20ld / %46s ",   field, va_arg (argp, long),               blank); goto found_it; }
+  if (!strcmp (mode, "%lld")){ snprintf (string, 81, "%-8s= %20lld / %46s ",  field, va_arg (argp, long long),          blank); goto found_it; }
+  if (!strcmp (mode, "%Ld")) { snprintf (string, 81, "%-8s= %20lld / %46s ",  field, va_arg (argp, long long),          blank); goto found_it; }
+  if (!strcmp (mode, "%u"))  { snprintf (string, 81, "%-8s= %20u / %46s ",    field, va_arg (argp, unsigned),           blank); goto found_it; }
+  if (!strcmp (mode, "%lu")) { snprintf (string, 81, "%-8s= %20lu / %46s ",   field, va_arg (argp, unsigned long),      blank); goto found_it; }
+  if (!strcmp (mode, "%llu")){ snprintf (string, 81, "%-8s= %20llu / %46s ",  field, va_arg (argp, unsigned long long), blank); goto found_it; }
+  if (!strcmp (mode, "%Lu")) { snprintf (string, 81, "%-8s= %20llu / %46s ",  field, va_arg (argp, unsigned long long), blank); goto found_it; }
+  if (!strcmp (mode, "%hd")) { snprintf (string, 81, "%-8s= %20d / %46s ",    field, va_arg (argp, int),                blank); goto found_it; }
+  if (!strcmp (mode, "%f"))  { snprintf (string, 81, "%-8s= %20.10f / %46s ", field, va_arg (argp, double),             blank); goto found_it; }
+  if (!strcmp (mode, "%lf")) { snprintf (string, 81, "%-8s= %20.10f / %46s ", field, va_arg (argp, double),             blank); goto found_it; }
+  if (!strcmp (mode, "%e"))  { snprintf (string, 81, "%-8s= %20.10E / %46s ", field, va_arg (argp, double),             blank); goto found_it; }
+  if (!strcmp (mode, "%le")) { snprintf (string, 81, "%-8s= %20.10E / %46s ", field, va_arg (argp, double),             blank); goto found_it; }
+  if (!strcmp (mode, "%g"))  { snprintf (string, 81, "%-8s= %20.10G / %46s ", field, va_arg (argp, double),             blank); goto found_it; }
+  if (!strcmp (mode, "%lg")) { snprintf (string, 81, "%-8s= %20.10G / %46s ", field, va_arg (argp, double),             blank); goto found_it; }
+  if (!strcmp (mode, "%jd")) { snprintf (string, 81, "%-8s= %20jd / %46s ",   field, va_arg (argp, intmax_t),           blank); goto found_it; }
 
   /* string value.  Quotes must be at least 18 chars apart.  Longer lines will this should be fixed to allow arbitrary string lengths, up to 69 chars */
@@ -67,8 +68,10 @@
     line[68] = 0;
     snprintf (string, 81, "%-8s= '%-18s' / %46s ", field, line, blank);
+    goto found_it;
   }
+  return (FALSE);
 
+found_it:
   strncpy (p, string, 80);
-  
   va_end (argp);
   return (TRUE);
Index: /branches/pap/Ohana/src/libfits/header/F_read_H.c
===================================================================
--- /branches/pap/Ohana/src/libfits/header/F_read_H.c	(revision 28483)
+++ /branches/pap/Ohana/src/libfits/header/F_read_H.c	(revision 28484)
@@ -54,7 +54,12 @@
   }
 
+  /* if these are not found in the header, they should be set to FALSE */
+  header[0].simple = FALSE;
+  header[0].extend = FALSE;
+
   header[0].unsign = gfits_get_unsign_mode();
   header[0].bscale = 1;
   header[0].bzero  = 0;
+
   for (i = 0; i < FT_MAX_NAXES; i++)
     header[0].Naxis[i] = 0;
@@ -69,14 +74,14 @@
   gfits_scan (header,  "BZERO",  "%lf",  1, &header[0].bzero);
 				       
-  gfits_scan (header,  "NAXIS1", "%lld", 1, (long long *) &header[0].Naxis[0]);
-  gfits_scan (header,  "NAXIS2", "%lld", 1, (long long *) &header[0].Naxis[1]);
-  gfits_scan (header,  "NAXIS3", "%lld", 1, (long long *) &header[0].Naxis[2]);
-  gfits_scan (header,  "NAXIS4", "%lld", 1, (long long *) &header[0].Naxis[3]);
-  gfits_scan (header,  "NAXIS5", "%lld", 1, (long long *) &header[0].Naxis[4]);
-  gfits_scan (header,  "NAXIS6", "%lld", 1, (long long *) &header[0].Naxis[5]);
-  gfits_scan (header,  "NAXIS7", "%lld", 1, (long long *) &header[0].Naxis[6]);
-  gfits_scan (header,  "NAXIS8", "%lld", 1, (long long *) &header[0].Naxis[7]);
-  gfits_scan (header,  "NAXIS9", "%lld", 1, (long long *) &header[0].Naxis[8]);
-  gfits_scan (header, "NAXIS10", "%lld", 1, (long long *) &header[0].Naxis[9]);
+  gfits_scan (header,  "NAXIS1", OFF_T_FMT, 1,  &header[0].Naxis[0]);
+  gfits_scan (header,  "NAXIS2", OFF_T_FMT, 1,  &header[0].Naxis[1]);
+  gfits_scan (header,  "NAXIS3", OFF_T_FMT, 1,  &header[0].Naxis[2]);
+  gfits_scan (header,  "NAXIS4", OFF_T_FMT, 1,  &header[0].Naxis[3]);
+  gfits_scan (header,  "NAXIS5", OFF_T_FMT, 1,  &header[0].Naxis[4]);
+  gfits_scan (header,  "NAXIS6", OFF_T_FMT, 1,  &header[0].Naxis[5]);
+  gfits_scan (header,  "NAXIS7", OFF_T_FMT, 1,  &header[0].Naxis[6]);
+  gfits_scan (header,  "NAXIS8", OFF_T_FMT, 1,  &header[0].Naxis[7]);
+  gfits_scan (header,  "NAXIS9", OFF_T_FMT, 1,  &header[0].Naxis[8]);
+  gfits_scan (header, "NAXIS10", OFF_T_FMT, 1,  &header[0].Naxis[9]);
 
   if (!gfits_scan (header, "PCOUNT",  "%d", 1, &header[0].pcount)) {
Index: /branches/pap/Ohana/src/libfits/header/F_scan.c
===================================================================
--- /branches/pap/Ohana/src/libfits/header/F_scan.c	(revision 28483)
+++ /branches/pap/Ohana/src/libfits/header/F_scan.c	(revision 28484)
@@ -84,4 +84,7 @@
   if (!strcmp (mode, "%hd"))  { *va_arg (argp, short *)     	     = value; return (TRUE); }
 
+  // XXX is this safe for 64bit off_t and 32bit off_t?
+  if (!strcmp (mode, "%jd"))  { *va_arg (argp, intmax_t *) 	     = value; return (TRUE); }
+
   /* no valid mode found */
   return (FALSE);
@@ -219,5 +222,5 @@
   if (!strcmp (mode, "%d"))   { *va_arg (argp, int *)       	     = value; return (TRUE); }
   if (!strcmp (mode, "%ld"))  { *va_arg (argp, long *)      	     = value; return (TRUE); }
-  if (!strcmp (mode, "%lld")) { *va_arg (argp, long long *) 	     = value; return (TRUE); }
+  if (!strcmp (mode, OFF_T_FMT)) { *va_arg (argp, long long *) 	     = value; return (TRUE); }
   if (!strcmp (mode, "%Ld"))  { *va_arg (argp, long long *) 	     = value; return (TRUE); }
   if (!strcmp (mode, "%u"))   { *va_arg (argp, unsigned *)  	     = value; return (TRUE); }
Index: /branches/pap/Ohana/src/libfits/include/gfitsio.h
===================================================================
--- /branches/pap/Ohana/src/libfits/include/gfitsio.h	(revision 28483)
+++ /branches/pap/Ohana/src/libfits/include/gfitsio.h	(revision 28484)
@@ -7,23 +7,9 @@
 # define GFITSIO
 
-/* also defined in libautocode/def/common.h */
-/* what about lin64 ?? - 'linux' might be defined automatically by linux */
 # ifndef BYTE_SWAP
-# ifdef linux
-# define BYTE_SWAP
+# ifndef NOT_BYTE_SWAP
+# error "neither BYTE_SWAP not NOT_BYTE_SWAP is set"
 # endif
-
-# ifdef sid
-# define BYTE_SWAP
 # endif
-
-# ifdef darwin_x86
-# define BYTE_SWAP
-# endif
-
-# ifdef dec
-# define BYTE_SWAP
-# endif
-# endif /* BYTE_SWAP */
 
 # ifndef NEWLINE
Index: /branches/pap/Ohana/src/libfits/matrix/F_add_M.c
===================================================================
--- /branches/pap/Ohana/src/libfits/matrix/F_add_M.c	(revision 28483)
+++ /branches/pap/Ohana/src/libfits/matrix/F_add_M.c	(revision 28484)
@@ -13,11 +13,11 @@
   if ((x + array[0].Naxis[0] > matrix[0].Naxis[0]) ||
       (y + array[0].Naxis[1] > matrix[0].Naxis[1])) {
-    fprintf (stderr, "can't add array here: (%lld,%lld) - (%lld,%lld) vs (%lld,%lld)\n",
-	     (long long) x, 
-	     (long long) y, 
-	     (long long) x + array[0].Naxis[0], 
-	     (long long) y + array[0].Naxis[1],
-	     (long long) matrix[0].Naxis[0], 
-	     (long long) matrix[0].Naxis[1]);
+    fprintf (stderr, "can't add array here: ("OFF_T_FMT","OFF_T_FMT") - ("OFF_T_FMT","OFF_T_FMT") vs ("OFF_T_FMT","OFF_T_FMT")\n",
+	      x, 
+	      y, 
+	      x + array[0].Naxis[0], 
+	      y + array[0].Naxis[1],
+	      matrix[0].Naxis[0], 
+	      matrix[0].Naxis[1]);
     return;
   }
Index: /branches/pap/Ohana/src/libfits/matrix/F_compress_M.c
===================================================================
--- /branches/pap/Ohana/src/libfits/matrix/F_compress_M.c	(revision 28483)
+++ /branches/pap/Ohana/src/libfits/matrix/F_compress_M.c	(revision 28484)
@@ -77,5 +77,5 @@
     snprintf (zaxis, 10, "ZNAXIS%d", i + 1);
     snprintf (naxis, 10, "NAXIS%d", i + 1);
-    MOD_KEYWORD_REQUIRED (zaxis,  naxis,  "%lld", (long long *) &header->Naxis[i], (long long) header->Naxis[i]);
+    MOD_KEYWORD_REQUIRED (zaxis,  naxis,  OFF_T_FMT,  &header->Naxis[i],  header->Naxis[i]);
   }    
 
@@ -203,7 +203,7 @@
   if (!gfits_varlength_column_define (ftable, &zdef, zcol)) ESCAPE;
   gfits_delete (header, "TFIELDS", 1);
-  snprintf (key, 10, "TTYPE%lld", (long long) zcol);
+  snprintf (key, 10, "TTYPE"OFF_T_FMT,  zcol);
   gfits_delete (header, key, 1);
-  snprintf (key, 10, "TFORM%lld", (long long) zcol);
+  snprintf (key, 10, "TFORM"OFF_T_FMT,  zcol);
   gfits_delete (header, key, 1);
 
Index: /branches/pap/Ohana/src/libfits/matrix/F_insert_M.c
===================================================================
--- /branches/pap/Ohana/src/libfits/matrix/F_insert_M.c	(revision 28483)
+++ /branches/pap/Ohana/src/libfits/matrix/F_insert_M.c	(revision 28484)
@@ -12,11 +12,11 @@
   if ((x + array[0].Naxis[0] > matrix[0].Naxis[0]) ||
       (y + array[0].Naxis[1] > matrix[0].Naxis[1])) {
-    fprintf (stderr, "can't insert array here: (%lld,%lld) - (%lld,%lld) vs (%lld,%lld)\n",
-	     (long long) x, 
-	     (long long) y, 
-	     (long long) x + array[0].Naxis[0], 
-	     (long long) y + array[0].Naxis[1],
-	     (long long) matrix[0].Naxis[0], 
-	     (long long) matrix[0].Naxis[1]);
+    fprintf (stderr, "can't insert array here: ("OFF_T_FMT","OFF_T_FMT") - ("OFF_T_FMT","OFF_T_FMT") vs ("OFF_T_FMT","OFF_T_FMT")\n",
+	      x, 
+	      y, 
+	      x + array[0].Naxis[0], 
+	      y + array[0].Naxis[1],
+	      matrix[0].Naxis[0], 
+	      matrix[0].Naxis[1]);
     return;
   }
Index: /branches/pap/Ohana/src/libfits/matrix/F_load_M.c
===================================================================
--- /branches/pap/Ohana/src/libfits/matrix/F_load_M.c	(revision 28483)
+++ /branches/pap/Ohana/src/libfits/matrix/F_load_M.c	(revision 28484)
@@ -95,5 +95,5 @@
   }
   if (nbytes != Nbytes) {  /* this is a FITS error, but often the image is OK */
-    fprintf (stderr, "incomplete block in FITS file: (%lld, %lld)\n", (long long) nbytes, (long long) Nbytes);
+    fprintf (stderr, "incomplete block in FITS file: ("OFF_T_FMT", "OFF_T_FMT")\n",  nbytes,  Nbytes);
     return (TRUE); 
   }
Index: /branches/pap/Ohana/src/libfits/matrix/F_read_portion.c
===================================================================
--- /branches/pap/Ohana/src/libfits/matrix/F_read_portion.c	(revision 28483)
+++ /branches/pap/Ohana/src/libfits/matrix/F_read_portion.c	(revision 28484)
@@ -108,9 +108,9 @@
 
   if (nbytes < Nbytes - 2880) {  /* this is a bad FITS error: image is not OK */
-    fprintf (stderr, "error reading in matrix data from FITS file %s (%lld < %lld - 2880)\n", filename, (long long) nbytes, (long long) Nbytes);
+    fprintf (stderr, "error reading in matrix data from FITS file %s ("OFF_T_FMT" < "OFF_T_FMT" - 2880)\n", filename,  nbytes,  Nbytes);
     return (FALSE);
   }
   if (nbytes != Nbytes) {  /* this is a FITS error, but often the image is OK */
-    fprintf (stderr, "incomplete block in %s: (%lld, %lld)\n", filename, (long long) nbytes, (long long) Nbytes);
+    fprintf (stderr, "incomplete block in %s: ("OFF_T_FMT", "OFF_T_FMT")\n", filename,  nbytes,  Nbytes);
     return (TRUE); 
   }
Index: /branches/pap/Ohana/src/libfits/matrix/F_read_segment.c
===================================================================
--- /branches/pap/Ohana/src/libfits/matrix/F_read_segment.c	(revision 28483)
+++ /branches/pap/Ohana/src/libfits/matrix/F_read_segment.c	(revision 28484)
@@ -148,9 +148,9 @@
 
   if (nbytes < Nbytes - 2880) {  /* this is a bad FITS error: image is not OK */
-    fprintf (stderr, "error reading in matrix data from FITS file (%lld < %lld - 2880)\n", (long long) nbytes, (long long) Nbytes);
+    fprintf (stderr, "error reading in matrix data from FITS file ("OFF_T_FMT" < "OFF_T_FMT" - 2880)\n",  nbytes,  Nbytes);
     return (FALSE);
   }
   if (nbytes != Nbytes) {  /* this is a FITS error, but often the image is OK */
-    fprintf (stderr, "incomplete block in FITS file: (%lld, %lld)\n", (long long) nbytes, (long long) Nbytes);
+    fprintf (stderr, "incomplete block in FITS file: ("OFF_T_FMT", "OFF_T_FMT")\n",  nbytes,  Nbytes);
     return (TRUE); 
   }
Index: /branches/pap/Ohana/src/libfits/table/F_create_TH.c
===================================================================
--- /branches/pap/Ohana/src/libfits/table/F_create_TH.c	(revision 28483)
+++ /branches/pap/Ohana/src/libfits/table/F_create_TH.c	(revision 28484)
@@ -27,5 +27,5 @@
   for (i = 0; i < header[0].Naxes; i++) {
     sprintf (axis, "NAXIS%d", i + 1);
-    gfits_modify (header,  axis, "%lld", 1, (long long) header[0].Naxis[i]);
+    gfits_modify (header,  axis, OFF_T_FMT, 1,  header[0].Naxis[i]);
   }
   
Index: /branches/pap/Ohana/src/libfits/table/F_define_column.c
===================================================================
--- /branches/pap/Ohana/src/libfits/table/F_define_column.c	(revision 28483)
+++ /branches/pap/Ohana/src/libfits/table/F_define_column.c	(revision 28484)
@@ -13,5 +13,5 @@
   Nfields = 0;
   gfits_scan (header, "TFIELDS", "%d", 1, &Nfields);
-  gfits_scan (header, "NAXIS1",  "%lld", 1, (long long *) &Naxis1);
+  gfits_scan (header, "NAXIS1",  OFF_T_FMT, 1,  &Naxis1);
   Nfields ++;
   Naxis1 += Nbytes*Nval;
@@ -35,5 +35,5 @@
   /* update TFIELDS & NAXIS1 */
   gfits_modify (header, "TFIELDS", "%d", 1, Nfields);
-  gfits_modify (header, "NAXIS1",  "%lld", 1, (long long) Naxis1);
+  gfits_modify (header, "NAXIS1",  OFF_T_FMT, 1,  Naxis1);
   header[0].Naxis[0] = Naxis1;
 
@@ -53,5 +53,5 @@
   Nfields = 0;
   gfits_scan (header, "TFIELDS", "%d", 1, &Nfields);
-  gfits_scan (header, "NAXIS1",  "%lld", 1, (long long *) &Naxis1);
+  gfits_scan (header, "NAXIS1",  OFF_T_FMT, 1,  &Naxis1);
   Nstart = Naxis1 + 1;
   Nfields ++;
@@ -70,5 +70,5 @@
 
   gfits_modify (header, "TFIELDS", "%d", 1, Nfields);
-  gfits_modify (header, "NAXIS1",  "%lld", 1, (long long) Naxis1);
+  gfits_modify (header, "NAXIS1",  OFF_T_FMT, 1,  Naxis1);
   header[0].Naxis[0] = Naxis1;
 
Index: /branches/pap/Ohana/src/libfits/table/F_get_column.c
===================================================================
--- /branches/pap/Ohana/src/libfits/table/F_get_column.c	(revision 28483)
+++ /branches/pap/Ohana/src/libfits/table/F_get_column.c	(revision 28484)
@@ -51,6 +51,6 @@
   
   /* check existing table dimensions */
-  gfits_scan (header, "NAXIS1",  "%lld", 1, (long long *) &Nx);
-  gfits_scan (header, "NAXIS2",  "%lld", 1, (long long *) &Ny);
+  gfits_scan (header, "NAXIS1",  OFF_T_FMT, 1,  &Nx);
+  gfits_scan (header, "NAXIS2",  OFF_T_FMT, 1,  &Ny);
 
   /* scan columns to find insert point */
@@ -187,6 +187,6 @@
   
   /* check existing table dimensions */
-  gfits_scan (header, "NAXIS1",  "%lld", 1, (long long *) &Nx);
-  gfits_scan (header, "NAXIS2",  "%lld", 1, (long long *) &Ny);
+  gfits_scan (header, "NAXIS1",  OFF_T_FMT, 1,  &Nx);
+  gfits_scan (header, "NAXIS2",  OFF_T_FMT, 1,  &Ny);
 
   /* scan columns to find insert point */
@@ -336,6 +336,6 @@
   
   /* check existing table dimensions */
-  gfits_scan (header, "NAXIS1",  "%lld", 1, (long long *) &Nx);
-  gfits_scan (header, "NAXIS2",  "%lld", 1, (long long *) &Ny);
+  gfits_scan (header, "NAXIS1",  OFF_T_FMT, 1,  &Nx);
+  gfits_scan (header, "NAXIS2",  OFF_T_FMT, 1,  &Ny);
 
   /* scan columns to find insert point */
Index: /branches/pap/Ohana/src/libfits/table/F_read_T.c
===================================================================
--- /branches/pap/Ohana/src/libfits/table/F_read_T.c	(revision 28483)
+++ /branches/pap/Ohana/src/libfits/table/F_read_T.c	(revision 28484)
@@ -64,5 +64,5 @@
     perror (string);
     if (Nread < gfits_data_min_size (table[0].header)) {
-      fprintf (stderr, "error: fits read error in %s, read %lld, need %lld\n", __func__, (long long) Nread, (long long) gfits_data_min_size (table[0].header));
+      fprintf (stderr, "error: fits read error in %s, read "OFF_T_FMT", need "OFF_T_FMT"\n", __func__,  Nread,  gfits_data_min_size (table[0].header));
       gfits_free_table  (table);
       return (FALSE);
@@ -109,5 +109,5 @@
   /* modify structure and header to match actual read rows Ny */
   table[0].header[0].Naxis[1] = Nrows;
-  gfits_modify (table[0].header, "NAXIS2",  "%lld", 1, (long long) Nrows);
+  gfits_modify (table[0].header, "NAXIS2",  OFF_T_FMT, 1,  Nrows);
   table[0].datasize = gfits_data_size (table[0].header);
 
@@ -185,6 +185,8 @@
     /* file pointer is at beginning of desired table data */
     start = ftello (f);
-    gfits_scan (header, "NAXIS1", "%lld", 1, (long long *) &Nx);
-    gfits_scan (header, "NAXIS2", "%lld", 1, (long long *) &Ny);
+
+    gfits_scan (header, "NAXIS1", OFF_T_FMT, 1, &Nx);
+    gfits_scan (header, "NAXIS2", OFF_T_FMT, 1, &Ny);
+
     for (i = 0; i < Nrow; i++) {
       if (row[i] > Ny) { return (FALSE); }
Index: /branches/pap/Ohana/src/libfits/table/F_read_TH.c
===================================================================
--- /branches/pap/Ohana/src/libfits/table/F_read_TH.c	(revision 28483)
+++ /branches/pap/Ohana/src/libfits/table/F_read_TH.c	(revision 28484)
@@ -70,14 +70,14 @@
   if (!status) return (FALSE);
 				                           
-  gfits_scan (Theader,  "NAXIS1", "%lld", 1, (long long *) &Theader[0].Naxis[0]);
-  gfits_scan (Theader,  "NAXIS2", "%lld", 1, (long long *) &Theader[0].Naxis[1]);
-  gfits_scan (Theader,  "NAXIS3", "%lld", 1, (long long *) &Theader[0].Naxis[2]);
-  gfits_scan (Theader,  "NAXIS4", "%lld", 1, (long long *) &Theader[0].Naxis[3]);
-  gfits_scan (Theader,  "NAXIS5", "%lld", 1, (long long *) &Theader[0].Naxis[4]);
-  gfits_scan (Theader,  "NAXIS6", "%lld", 1, (long long *) &Theader[0].Naxis[5]);
-  gfits_scan (Theader,  "NAXIS7", "%lld", 1, (long long *) &Theader[0].Naxis[6]);
-  gfits_scan (Theader,  "NAXIS8", "%lld", 1, (long long *) &Theader[0].Naxis[7]);
-  gfits_scan (Theader,  "NAXIS9", "%lld", 1, (long long *) &Theader[0].Naxis[8]);
-  gfits_scan (Theader, "NAXIS10", "%lld", 1, (long long *) &Theader[0].Naxis[9]);
+  gfits_scan (Theader,  "NAXIS1", OFF_T_FMT, 1,  &Theader[0].Naxis[0]);
+  gfits_scan (Theader,  "NAXIS2", OFF_T_FMT, 1,  &Theader[0].Naxis[1]);
+  gfits_scan (Theader,  "NAXIS3", OFF_T_FMT, 1,  &Theader[0].Naxis[2]);
+  gfits_scan (Theader,  "NAXIS4", OFF_T_FMT, 1,  &Theader[0].Naxis[3]);
+  gfits_scan (Theader,  "NAXIS5", OFF_T_FMT, 1,  &Theader[0].Naxis[4]);
+  gfits_scan (Theader,  "NAXIS6", OFF_T_FMT, 1,  &Theader[0].Naxis[5]);
+  gfits_scan (Theader,  "NAXIS7", OFF_T_FMT, 1,  &Theader[0].Naxis[6]);
+  gfits_scan (Theader,  "NAXIS8", OFF_T_FMT, 1,  &Theader[0].Naxis[7]);
+  gfits_scan (Theader,  "NAXIS9", OFF_T_FMT, 1,  &Theader[0].Naxis[8]);
+  gfits_scan (Theader, "NAXIS10", OFF_T_FMT, 1,  &Theader[0].Naxis[9]);
 
   return (TRUE);
Index: /branches/pap/Ohana/src/libfits/table/F_set_column.c
===================================================================
--- /branches/pap/Ohana/src/libfits/table/F_set_column.c	(revision 28483)
+++ /branches/pap/Ohana/src/libfits/table/F_set_column.c	(revision 28484)
@@ -49,10 +49,10 @@
   
   /* check existing table dimensions */
-  gfits_scan (header, "NAXIS1", "%lld", 1, (long long *) &Nx);
-  gfits_scan (header, "NAXIS2", "%lld", 1, (long long *) &Ny);
+  gfits_scan (header, "NAXIS1", OFF_T_FMT, 1,  &Nx);
+  gfits_scan (header, "NAXIS2", OFF_T_FMT, 1,  &Ny);
   if (Ny == 0) { 
     Ny = Nrow;
     header[0].Naxis[1] = Ny;
-    gfits_modify (header, "NAXIS2", "%lld", 1, (long long) Ny);
+    gfits_modify (header, "NAXIS2", OFF_T_FMT, 1,  Ny);
 
     nbytes = gfits_data_size (header);
@@ -181,10 +181,10 @@
   
   /* check existing table dimensions */
-  gfits_scan (header, "NAXIS1", "%lld", 1, (long long *) &Nx);
-  gfits_scan (header, "NAXIS2", "%lld", 1, (long long *) &Ny);
+  gfits_scan (header, "NAXIS1", OFF_T_FMT, 1,  &Nx);
+  gfits_scan (header, "NAXIS2", OFF_T_FMT, 1,  &Ny);
   if (Ny == 0) { 
     Ny = Nrow;
     header[0].Naxis[1] = Ny;
-    gfits_modify (header, "NAXIS2", "%lld", 1, (long long) Ny);
+    gfits_modify (header, "NAXIS2", OFF_T_FMT, 1,  Ny);
 
     nbytes = gfits_data_size (header);
Index: /branches/pap/Ohana/src/libfits/table/F_table_format.c
===================================================================
--- /branches/pap/Ohana/src/libfits/table/F_table_format.c	(revision 28483)
+++ /branches/pap/Ohana/src/libfits/table/F_table_format.c	(revision 28484)
@@ -130,6 +130,6 @@
   off_t i, Nx, Ny;
 
-  gfits_scan (ftable[0].header, "NAXIS1", "%lld", 1, (long long *) &Nx);
-  gfits_scan (ftable[0].header, "NAXIS2", "%lld", 1, (long long *) &Ny);
+  gfits_scan (ftable[0].header, "NAXIS1", OFF_T_FMT, 1,  &Nx);
+  gfits_scan (ftable[0].header, "NAXIS2", OFF_T_FMT, 1,  &Ny);
   
   if (start + Nkeep > Ny) return (FALSE);
@@ -158,6 +158,6 @@
   off_t i, N, Nx, Ny;
 
-  gfits_scan (ftable[0].header, "NAXIS1", "%lld", 1, (long long *) &Nx);
-  gfits_scan (ftable[0].header, "NAXIS2", "%lld", 1, (long long *) &Ny);
+  gfits_scan (ftable[0].header, "NAXIS1", OFF_T_FMT, 1,  &Nx);
+  gfits_scan (ftable[0].header, "NAXIS2", OFF_T_FMT, 1,  &Ny);
 
   /* make empty vtable from table */
@@ -191,5 +191,5 @@
   va_start (argp, table);
 
-  gfits_scan (table[0].header, "NAXIS1",  "%lld", 1, (long long *) &Nx);
+  gfits_scan (table[0].header, "NAXIS1",  OFF_T_FMT, 1,  &Nx);
   gfits_scan (table[0].header, "TFIELDS", "%d", 1, &Nfields);
 
@@ -236,6 +236,6 @@
   off = 0;
 
-  gfits_scan (ftable[0].header, "NAXIS1",  "%lld", 1, (long long *) &Nx);
-  gfits_scan (ftable[0].header, "NAXIS2",  "%lld", 1, (long long *) &Ny);
+  gfits_scan (ftable[0].header, "NAXIS1",  OFF_T_FMT, 1,  &Nx);
+  gfits_scan (ftable[0].header, "NAXIS2",  OFF_T_FMT, 1,  &Ny);
   gfits_scan (ftable[0].header, "TFIELDS", "%d", 1, &Nfields);
 
@@ -320,6 +320,6 @@
   off = 0;
 
-  gfits_scan (ftable[0].header, "NAXIS1",  "%lld", 1, (long long *) &Nx);
-  gfits_scan (ftable[0].header, "NAXIS2",  "%lld", 1, (long long *) &Ny);
+  gfits_scan (ftable[0].header, "NAXIS1",  OFF_T_FMT, 1,  &Nx);
+  gfits_scan (ftable[0].header, "NAXIS2",  OFF_T_FMT, 1,  &Ny);
   gfits_scan (ftable[0].header, "TFIELDS", "%d", 1, &Nfields);
 
Index: /branches/pap/Ohana/src/libfits/table/F_table_row.c
===================================================================
--- /branches/pap/Ohana/src/libfits/table/F_table_row.c	(revision 28483)
+++ /branches/pap/Ohana/src/libfits/table/F_table_row.c	(revision 28484)
@@ -11,6 +11,6 @@
   header = table[0].header;
 
-  gfits_scan (header, "NAXIS1", "%lld", 1, (long long *) &Nx);
-  gfits_scan (header, "NAXIS2", "%lld", 1, (long long *) &Ny);
+  gfits_scan (header, "NAXIS1", OFF_T_FMT, 1,  &Nx);
+  gfits_scan (header, "NAXIS2", OFF_T_FMT, 1,  &Ny);
   
   if (header[0].Naxis[1] != Ny) return (FALSE);
@@ -23,5 +23,5 @@
   Ny += Nrow;
   header[0].Naxis[1] = Ny;
-  gfits_modify (header, "NAXIS2",  "%lld", 1, (long long) Ny);
+  gfits_modify (header, "NAXIS2",  OFF_T_FMT, 1,  Ny);
 
   nbytes = gfits_data_size (header);
@@ -43,6 +43,6 @@
   header = table[0].header;
 
-  gfits_scan (header, "NAXIS1", "%lld", 1, (long long *) &Nx);
-  gfits_scan (header, "NAXIS2", "%lld", 1, (long long *) &Ny);
+  gfits_scan (header, "NAXIS1", OFF_T_FMT, 1,  &Nx);
+  gfits_scan (header, "NAXIS2", OFF_T_FMT, 1,  &Ny);
   
   if (header[0].Naxis[1] != Ny) return (FALSE);
@@ -64,5 +64,5 @@
   Ny += Nrow;
   header[0].Naxis[1] = Ny;
-  gfits_modify (header, "NAXIS2", "%lld", 1, (long long) Ny);
+  gfits_modify (header, "NAXIS2", OFF_T_FMT, 1,  Ny);
 
   table[0].datasize = gfits_data_size (table[0].header);
@@ -81,6 +81,6 @@
   header = table[0].header;
 
-  gfits_scan (header, "NAXIS1", "%lld", 1, (long long *) &Nx);
-  gfits_scan (header, "NAXIS2", "%lld", 1, (long long *) &Ny);
+  gfits_scan (header, "NAXIS1", OFF_T_FMT, 1,  &Nx);
+  gfits_scan (header, "NAXIS2", OFF_T_FMT, 1,  &Ny);
   
   if (header[0].Naxis[1] != Ny) return (FALSE);
@@ -97,5 +97,5 @@
   Ny -= Nrow;
   header[0].Naxis[1] = Ny;
-  gfits_modify (header, "NAXIS2", "%lld", 1, (long long) Ny);
+  gfits_modify (header, "NAXIS2", OFF_T_FMT, 1,  Ny);
 
   nbytes = gfits_data_size (header);
Index: /branches/pap/Ohana/src/libfits/table/F_table_varlength.c
===================================================================
--- /branches/pap/Ohana/src/libfits/table/F_table_varlength.c	(revision 28483)
+++ /branches/pap/Ohana/src/libfits/table/F_table_varlength.c	(revision 28484)
@@ -64,5 +64,5 @@
 
   // heap_start must be long long so file may be very large
-  if (!gfits_scan (ftable->header, "THEAP", "%lld", 1, (long long *) &def->heap_start)) {
+  if (!gfits_scan (ftable->header, "THEAP", OFF_T_FMT, 1,  &def->heap_start)) {
     def->heap_start = ftable->header->Naxis[0]*ftable->header->Naxis[1];
   }
Index: /branches/pap/Ohana/src/libfits/table/F_write_T.c
===================================================================
--- /branches/pap/Ohana/src/libfits/table/F_write_T.c	(revision 28483)
+++ /branches/pap/Ohana/src/libfits/table/F_write_T.c	(revision 28484)
@@ -40,6 +40,6 @@
   Nrow = table[0].Nrow;
   row = table[0].row;
-  gfits_scan (table[0].header, "NAXIS1", "%lld", 1, (long long *) &Nx);
-  gfits_scan (table[0].header, "NAXIS2", "%lld", 1, (long long *) &Ny);
+  gfits_scan (table[0].header, "NAXIS1", OFF_T_FMT, 1,  &Nx);
+  gfits_scan (table[0].header, "NAXIS2", OFF_T_FMT, 1,  &Ny);
 
   /* file pointer is at beginning of desired table data */
@@ -79,5 +79,5 @@
   
   /* modify vtable to represent full disk table */
-  gfits_modify (ftable[0].header, "NAXIS2", "%lld", 1, (long long) Ntotal);
+  gfits_modify (ftable[0].header, "NAXIS2", OFF_T_FMT, 1,  Ntotal);
   ftable[0].header[0].Naxis[1] = Ntotal;
 
Index: /branches/pap/Ohana/src/libohana/Makefile
===================================================================
--- /branches/pap/Ohana/src/libohana/Makefile	(revision 28483)
+++ /branches/pap/Ohana/src/libohana/Makefile	(revision 28484)
@@ -1,5 +1,5 @@
 default: install
 help:
-	@echo "make options: install libohana clean dist"
+	@echo "make options: install libohana clean dist test typetest"
 
 include ../../Makefile.System
@@ -19,5 +19,5 @@
 TFLAGS        = $(FULL_CFLAGS) $(FULL_CPPFLAGS) $(FULL_LDFLAGS) -lohana -ltap_ohana
 
-install: $(DESTLIB)/libohana.a $(DESTLIB)/libohana.$(DLLTYPE)
+install: $(DESTLIB)/libohana.a $(DESTLIB)/libohana.$(DLLTYPE) typetest
 libohana: $(LIB)/libohana.$(ARCH).a $(LIB)/libohana.$(ARCH).$(DLLTYPE)
 
@@ -40,4 +40,12 @@
 $(SRC)/CommOps.$(ARCH).o	 \
 $(SRC)/version.$(ARCH).o
+
+TYPETEST = \
+$(TESTDIR)/typetest.$(ARCH)
+
+$(TYPETEST) : $(LIB)/libohana.$(ARCH).a
+
+typetest: $(TYPETEST)
+	for i in $(TYPETEST); do $$i || exit 1; done
 
 TEST = \
Index: /branches/pap/Ohana/src/libohana/include/ohana.h
===================================================================
--- /branches/pap/Ohana/src/libohana/include/ohana.h	(revision 28483)
+++ /branches/pap/Ohana/src/libohana/include/ohana.h	(revision 28484)
@@ -94,4 +94,31 @@
 };
 
+/* note: in the Ohana tree, the byte order is determined by the ARCH variable
+   if you have a small endian machine that is not listed here, the test
+   program 'typestest' should fail */
+# ifndef BYTE_SWAP
+# ifdef linux
+# define BYTE_SWAP
+# endif
+
+# ifdef lin64
+# define BYTE_SWAP
+# endif
+
+# ifdef sid
+# define BYTE_SWAP
+# endif
+
+# if defined(darwin_x86) || defined(_DARWIN_C_SOURCE)
+# define BYTE_SWAP
+# endif
+
+# ifdef dec
+# define BYTE_SWAP
+# endif
+# else
+# define NOT_BYTE_SWAP
+# endif /* BYTE_SWAP */
+
 # ifndef NAN
 # ifndef BYTE_SWAP
@@ -103,4 +130,22 @@
     __attribute_used__ = { __nan_bytes };
 # define NAN    (__nan_union.__d)
+# endif
+
+/* if your build crashes on OFF_T_MODE, you probably need to add your 64bit hardware to this list */
+# ifdef _LARGEFILE_SOURCE
+# define OFF_T_FMT "%jd"
+# endif
+# ifdef lin64
+# define OFF_T_FMT "%jd"
+# endif
+# ifdef _DARWIN_C_SOURCE
+# define OFF_T_FMT "%jd"
+#endif
+# ifndef OFF_T_FMT
+# define OFF_T_FMT "%ld"
+# endif
+
+# ifndef isfinite
+# define isfinite(A) (!isnan(A))
 # endif
 
Index: /branches/pap/Ohana/src/libohana/test/typetest.c
===================================================================
--- /branches/pap/Ohana/src/libohana/test/typetest.c	(revision 28484)
+++ /branches/pap/Ohana/src/libohana/test/typetest.c	(revision 28484)
@@ -0,0 +1,73 @@
+# include "ohana.h"
+
+enum
+{
+    OHANA_LITTLE_ENDIAN = 0x03020100ul,
+    OHANA_BIG_ENDIAN    = 0x00010203ul,
+};
+
+static const union { 
+    unsigned char bytes[4]; 
+    int value; 
+} ohana_host_order = { { 0, 1, 2, 3 } };
+
+# define OHANA_HOST_ORDER (ohana_host_order.value)
+
+// we need to verify some type-related issues:
+int main (int argc, char **argv) {
+
+    int status;
+
+    status = 0;
+
+    // 1) have we defined BYTE_SWAP correctly?
+
+# ifdef BYTE_SWAP
+    if (OHANA_HOST_ORDER == OHANA_BIG_ENDIAN) {
+	fprintf (stderr, "ERROR: BYTE_SWAP is defined on a big endian machine\n");
+	fprintf (stderr, "(see libohana/include/ohana.h)\n");
+	status = 1;
+    } else {
+	fprintf (stderr, "BYTE_SWAP is small endian\n");
+    }	
+# else
+    if (OHANA_HOST_ORDER == OHANA_BIG_ENDIAN) {
+	fprintf (stderr, "NOT_BYTE_SWAP is big endian\n");
+    } else {
+	fprintf (stderr, "ERROR: BYTE_SWAP not defined on a small endian machine\n");
+	fprintf (stderr, "(see libohana/include/ohana.h)\n");
+	status = 1;
+    }	
+# endif 
+
+    // 2) have we defined NAN correctly?
+    {
+	float fvalue;
+	double dvalue;
+    
+	fvalue = NAN;
+	dvalue = NAN;
+
+	if (isfinite(fvalue)) {
+	    fprintf (stderr, "ERROR: NAN definition fails for float\n");
+	    fprintf (stderr, "fvalue: %e\n", fvalue);
+	    status = 1;
+	}
+	if (isfinite(dvalue)) {
+	    fprintf (stderr, "ERROR: NAN definition fails for double\n");
+	    fprintf (stderr, "dvalue: %e\n", dvalue);
+	    status = 1;
+	}
+
+	fprintf (stderr, "fvalue: %e (isfinite: %d)\n", fvalue, isfinite(fvalue));
+	fprintf (stderr, "dvalue: %e (isfinite: %d)\n", dvalue, isfinite(dvalue));
+
+# ifdef __STDC_VERSION__
+	fprintf (stderr, "STDC_VERSION: %ld\n", __STDC_VERSION__);
+# else
+	fprintf (stderr, "STDC_VERSION is not set\n");
+# endif
+
+    }
+    exit (status);
+}
Index: /branches/pap/Ohana/src/opihi/cmd.data/create.c
===================================================================
--- /branches/pap/Ohana/src/opihi/cmd.data/create.c	(revision 28483)
+++ /branches/pap/Ohana/src/opihi/cmd.data/create.c	(revision 28484)
@@ -4,5 +4,5 @@
   
   int i, N, INT;
-  float start, end, delta;
+  opihi_flt start, end, delta;
   Vector *vec;
   
Index: /branches/pap/Ohana/src/opihi/cmd.data/extract.c
===================================================================
--- /branches/pap/Ohana/src/opihi/cmd.data/extract.c	(revision 28483)
+++ /branches/pap/Ohana/src/opihi/cmd.data/extract.c	(revision 28484)
@@ -70,6 +70,6 @@
     if ((out[0].header.Naxis[1] != Ny) || (out[0].header.Naxis[0] != Nx)) {
       gprint (GP_ERR, "matrix sizes mis-matched\n");
-      gprint (GP_ERR, "%d x %d  vs  %lld x %lld\n", Nx, Ny, 
-	      (long long) out[0].header.Naxis[0], (long long) out[0].header.Naxis[1]);
+      gprint (GP_ERR, "%d x %d  vs  "OFF_T_FMT" x "OFF_T_FMT"\n", Nx, Ny, 
+	       out[0].header.Naxis[0],  out[0].header.Naxis[1]);
       return (FALSE);
     }
Index: /branches/pap/Ohana/src/opihi/cmd.data/help/histogram
===================================================================
--- /branches/pap/Ohana/src/opihi/cmd.data/help/histogram	(revision 28483)
+++ /branches/pap/Ohana/src/opihi/cmd.data/help/histogram	(revision 28484)
@@ -1,8 +1,19 @@
 
-   histogram <buffer> <x> <y> [-region sx sy nx ny] [-range min max]
+   histogram <invec> <outvec> <start> <end> [<delta>] [-range <dx_outvec>]
 
-   calculate a histogram of the image pixel values in the given
-   buffer, optionally constrained to the given region, with optional
-   max and min values.  the results are placed in the vectors x and y,
-   which contain the pixel values and the number of occurences.
+   calculate a histogram of the <invec> values and store the
+   occurrences count in the <outvec> buffer. Optionally constrained to
+   the given <start>-<end> region with <delta> step value (default
+   step is 1). The optional '-range <dx_outvec>' parameter allows storing
+   the range <start>-<end> values with <delta> increment
 
+   Sample code usage:
+   
+   # create a vector ('x') containing arbitrary values  [0.:1.] range
+   create y 0 100 1; set x = sin(y)
+
+   # build histogram from x from 0. to 1. with 0.1 delta step
+   histogram x xhist 0. 1. .1 -range dx
+
+   # plot corresponding histogram
+   limits dx xhist; clear; box; plot dx xhist -x 1
Index: /branches/pap/Ohana/src/opihi/cmd.data/help/imhist
===================================================================
--- /branches/pap/Ohana/src/opihi/cmd.data/help/imhist	(revision 28484)
+++ /branches/pap/Ohana/src/opihi/cmd.data/help/imhist	(revision 28484)
@@ -0,0 +1,8 @@
+
+   imhist <buffer> <x> <y> [-region sx sy nx ny] [-range min max]
+
+   calculate a histogram of the image pixel values in the given
+   buffer, optionally constrained to the given region, with optional
+   max and min values.  the results are placed in the vectors x and y,
+   which contain the pixel values and the number of occurences.
+
Index: /branches/pap/Ohana/src/opihi/cmd.data/matrix.c
===================================================================
--- /branches/pap/Ohana/src/opihi/cmd.data/matrix.c	(revision 28483)
+++ /branches/pap/Ohana/src/opihi/cmd.data/matrix.c	(revision 28484)
@@ -23,7 +23,7 @@
 
     if (bufB[0].matrix.Naxis[0] != bufC[0].matrix.Naxis[1]) {
-      gprint (GP_ERR, "size mis-match in matrices: (%lld x %lld) * (%lld x %lld)\n", 
-	      (long long) bufB[0].matrix.Naxis[0], (long long) bufB[0].matrix.Naxis[1], 
-	      (long long) bufC[0].matrix.Naxis[0], (long long) bufC[0].matrix.Naxis[1]);
+      gprint (GP_ERR, "size mis-match in matrices: ("OFF_T_FMT" x "OFF_T_FMT") * ("OFF_T_FMT" x "OFF_T_FMT")\n", 
+	       bufB[0].matrix.Naxis[0],  bufB[0].matrix.Naxis[1], 
+	       bufC[0].matrix.Naxis[0],  bufC[0].matrix.Naxis[1]);
       return (FALSE);
     }
Index: /branches/pap/Ohana/src/opihi/cmd.data/rd.c
===================================================================
--- /branches/pap/Ohana/src/opihi/cmd.data/rd.c	(revision 28483)
+++ /branches/pap/Ohana/src/opihi/cmd.data/rd.c	(revision 28484)
@@ -222,6 +222,6 @@
   buf[0].unsign = buf[0].header.unsign;
 
-  gprint (GP_LOG, "read %lld bytes from %s into buffer %s\n", 
-	  (long long) buf[0].header.datasize + buf[0].matrix.datasize, argv[2], argv[1]);
+  gprint (GP_LOG, "read "OFF_T_FMT" bytes from %s into buffer %s\n", 
+	   buf[0].header.datasize + buf[0].matrix.datasize, argv[2], argv[1]);
 
   blank = 0xffff;
Index: /branches/pap/Ohana/src/opihi/cmd.data/rdseg.c
===================================================================
--- /branches/pap/Ohana/src/opihi/cmd.data/rdseg.c	(revision 28483)
+++ /branches/pap/Ohana/src/opihi/cmd.data/rdseg.c	(revision 28484)
@@ -62,5 +62,5 @@
   buf[0].bzero  = buf[0].header.bzero;     /* store the original values */
   buf[0].unsign = buf[0].header.unsign;
-  gprint (GP_LOG, "read %lld bytes from %s into buffer %s\n", (long long) buf[0].header.datasize + buf[0].matrix.datasize, argv[2], argv[1]);
+  gprint (GP_LOG, "read "OFF_T_FMT" bytes from %s into buffer %s\n",  buf[0].header.datasize + buf[0].matrix.datasize, argv[2], argv[1]);
 
   gfits_scan (&buf[0].header, "BLANK", "%d", 1, &blank);
Index: /branches/pap/Ohana/src/opihi/cmd.data/rebin.c
===================================================================
--- /branches/pap/Ohana/src/opihi/cmd.data/rebin.c	(revision 28483)
+++ /branches/pap/Ohana/src/opihi/cmd.data/rebin.c	(revision 28484)
@@ -63,5 +63,5 @@
     }      
   }
-  if (VERBOSE) gprint (GP_LOG, "rebin %s to %s (%lld,%lld to %d,%d)\n", argv[1], argv[2], (long long) in[0].header.Naxis[0], (long long) in[0].header.Naxis[1], nx, ny);
+  if (VERBOSE) gprint (GP_LOG, "rebin %s to %s ("OFF_T_FMT","OFF_T_FMT" to %d,%d)\n", argv[1], argv[2],  in[0].header.Naxis[0],  in[0].header.Naxis[1], nx, ny);
 
   Nx = in[0].header.Naxis[0];
Index: /branches/pap/Ohana/src/opihi/dvo/avextract.c
===================================================================
--- /branches/pap/Ohana/src/opihi/dvo/avextract.c	(revision 28483)
+++ /branches/pap/Ohana/src/opihi/dvo/avextract.c	(revision 28484)
@@ -103,5 +103,5 @@
     catalog.Nsecfilt = 0;
 
-    if (VERBOSE) gprint (GP_ERR, "trying %s (%lld of %lld)\n", catalog.filename, (long long) i, (long long) skylist[0].Nregions);
+    if (VERBOSE) gprint (GP_ERR, "trying %s ("OFF_T_FMT" of "OFF_T_FMT")\n", catalog.filename,  i,  skylist[0].Nregions);
       
     // an error exit status here is a significant error
Index: /branches/pap/Ohana/src/opihi/dvo/avmatch.c
===================================================================
--- /branches/pap/Ohana/src/opihi/dvo/avmatch.c	(revision 28483)
+++ /branches/pap/Ohana/src/opihi/dvo/avmatch.c	(revision 28484)
@@ -102,5 +102,5 @@
     catalog.Nsecfilt = 0;
 
-    if (VERBOSE) gprint (GP_ERR, "trying %s (%lld of %lld)\n", catalog.filename, (long long) i, (long long) skylist[0].Nregions);
+    if (VERBOSE) gprint (GP_ERR, "trying %s ("OFF_T_FMT" of "OFF_T_FMT")\n", catalog.filename,  i,  skylist[0].Nregions);
       
     // an error exit status here is a significant error
Index: /branches/pap/Ohana/src/opihi/dvo/cmatch.c
===================================================================
--- /branches/pap/Ohana/src/opihi/dvo/cmatch.c	(revision 28483)
+++ /branches/pap/Ohana/src/opihi/dvo/cmatch.c	(revision 28484)
@@ -37,5 +37,5 @@
   }
   dvo_catalog_unlock (&catalog1);
-  gprint (GP_ERR, "read %lld stars from phot catalog file %s\n", (long long) catalog1.Naverage, filename);
+  gprint (GP_ERR, "read "OFF_T_FMT" stars from phot catalog file %s\n",  catalog1.Naverage, filename);
 
   /* this is for loading from a text file, presumably hstgsc or usno
Index: /branches/pap/Ohana/src/opihi/dvo/cmpread.c
===================================================================
--- /branches/pap/Ohana/src/opihi/dvo/cmpread.c	(revision 28483)
+++ /branches/pap/Ohana/src/opihi/dvo/cmpread.c	(revision 28484)
@@ -65,5 +65,5 @@
 
   /* find expected number of stars */
-  if (!gfits_scan (&header, "NSTARS", "%lld", 1, (long long *) &Nstars)) {
+  if (!gfits_scan (&header, "NSTARS", OFF_T_FMT, 1,  &Nstars)) {
     gprint (GP_ERR, "ERROR: can't get NSTARS from header\n");
     gfits_free_header (&header);
@@ -77,5 +77,5 @@
     /* allocate space for stars */
     gprint (GP_ERR, "reading from TEXT cmp file %s\n", argv[2]);
-    if (!gfits_scan (&header, "NSTARS", "%lld", 1, (long long *) &Nstars)) {
+    if (!gfits_scan (&header, "NSTARS", OFF_T_FMT, 1,  &Nstars)) {
       gprint (GP_ERR, "ERROR: failed to find NSTARS\n");
       exit (1);
@@ -139,5 +139,5 @@
   free (stars);
   gfits_free_header (&header);
-  gprint (GP_ERR, "loaded %lld objects\n", (long long) Nstars);
+  gprint (GP_ERR, "loaded "OFF_T_FMT" objects\n",  Nstars);
   return (TRUE);
 }
Index: /branches/pap/Ohana/src/opihi/dvo/dbExtractMeasures.c
===================================================================
--- /branches/pap/Ohana/src/opihi/dvo/dbExtractMeasures.c	(revision 28483)
+++ /branches/pap/Ohana/src/opihi/dvo/dbExtractMeasures.c	(revision 28484)
@@ -303,4 +303,7 @@
     case MEAS_AZ: /* OK */
       value.Flt = measure[0].az;
+      break;
+    case MEAS_ALT: /* OK */
+      value.Flt = 90.0 - DEG_RAD*acos(1.0/measure[0].airmass);
       break;
     case MEAS_EXPTIME: /* OK */
Index: /branches/pap/Ohana/src/opihi/dvo/detrend.c
===================================================================
--- /branches/pap/Ohana/src/opihi/dvo/detrend.c	(revision 28483)
+++ /branches/pap/Ohana/src/opihi/dvo/detrend.c	(revision 28484)
@@ -137,5 +137,5 @@
 
   /* load existing data from database */
-  gfits_scan (&header, "NIMAGES", "%lld", 1, (long long *) &Nimage);
+  gfits_scan (&header, "NIMAGES", OFF_T_FMT, 1,  &Nimage);
   ALLOCATE (pimage, RegImage, Nimage);
   status = fread (pimage, sizeof(RegImage), Nimage, f);
@@ -143,5 +143,5 @@
 
   if (status != Nimage) {
-    gprint (GP_ERR, "ERROR: header and data in dB don't match (%lld vs %lld)\n", (long long) Nimage, (long long) status);
+    gprint (GP_ERR, "ERROR: header and data in dB don't match ("OFF_T_FMT" vs "OFF_T_FMT")\n",  Nimage,  status);
     gfits_free_header (&header);
     free (pimage);
Index: /branches/pap/Ohana/src/opihi/dvo/fitcolors.c
===================================================================
--- /branches/pap/Ohana/src/opihi/dvo/fitcolors.c	(revision 28483)
+++ /branches/pap/Ohana/src/opihi/dvo/fitcolors.c	(revision 28484)
@@ -174,5 +174,5 @@
     // the selection criteria
   }
-  gprint (GP_ERR, "using %lld possible regions\n", (long long) skylist[0].Nregions);
+  gprint (GP_ERR, "using "OFF_T_FMT" possible regions\n",  skylist[0].Nregions);
 
   /* vectors to save data */
Index: /branches/pap/Ohana/src/opihi/dvo/fitsed.c
===================================================================
--- /branches/pap/Ohana/src/opihi/dvo/fitsed.c	(revision 28483)
+++ /branches/pap/Ohana/src/opihi/dvo/fitsed.c	(revision 28484)
@@ -185,5 +185,5 @@
   /* loop over regions, extract data for each region */
   // XXX add interrupt checks
-  gprint (GP_ERR, "using %lld possible regions\n", (long long) skylist[0].Nregions);
+  gprint (GP_ERR, "using "OFF_T_FMT" possible regions\n",  skylist[0].Nregions);
   for (k = 0; k < skylist[0].Nregions; k++) {
     /* lock, load, unlock catalog */
Index: /branches/pap/Ohana/src/opihi/dvo/gstar.c
===================================================================
--- /branches/pap/Ohana/src/opihi/dvo/gstar.c	(revision 28483)
+++ /branches/pap/Ohana/src/opihi/dvo/gstar.c	(revision 28484)
@@ -159,5 +159,5 @@
       k = N1[i];
       if (!QUIET) {
-	gprint (GP_LOG, "star: %lld\n", (long long) k);
+	gprint (GP_LOG, "star: "OFF_T_FMT"\n",  i);
 	gprint (GP_LOG, "%11.7f ", catalog.average[k].R);
 	gprint (GP_LOG, "%11.7f ", catalog.average[k].D);
@@ -165,5 +165,7 @@
 	gprint (GP_LOG, "%3d   ",  catalog.average[k].Nmeasure);
 	gprint (GP_LOG, "%4.1f ",  0.01*catalog.average[k].Xp);
-	gprint (GP_LOG, "%5d ",     catalog.average[k].flags);
+	gprint (GP_LOG, "%5x ",    catalog.average[k].flags);
+	gprint (GP_LOG, "%x ",     catalog.average[k].objID);
+	gprint (GP_LOG, "%x ",     catalog.average[k].catID);
 	
 	if (FULL_OUTPUT) {
@@ -176,6 +178,4 @@
 	    gprint (GP_LOG, "%f ",     catalog.average[k].P);
 	    gprint (GP_LOG, "%f ",     catalog.average[k].dP);
-	    gprint (GP_LOG, "%x ",     catalog.average[k].objID);
-	    gprint (GP_LOG, "%x ",     catalog.average[k].catID);
 	}
 
@@ -244,6 +244,4 @@
 		gprint (GP_LOG, "%f ", catalog.measure[m].crNsigma);
 		gprint (GP_LOG, "%f ", catalog.measure[m].extNsigma);
-		gprint (GP_LOG, "%f ", 0.01*catalog.measure[m].FWx);
-		gprint (GP_LOG, "%f ", 0.01*catalog.measure[m].FWy);
 		gprint (GP_LOG, "%f ", (360.0/(float)0xffff)*catalog.measure[m].theta);
 	    }
Index: /branches/pap/Ohana/src/opihi/dvo/imrough.c
===================================================================
--- /branches/pap/Ohana/src/opihi/dvo/imrough.c	(revision 28483)
+++ /branches/pap/Ohana/src/opihi/dvo/imrough.c	(revision 28484)
@@ -267,5 +267,5 @@
     
     /* load existing data from database */
-    gfits_scan (&header, "NIMAGES", "%lld", 1, (long long *) &Nimage);
+    gfits_scan (&header, "NIMAGES", OFF_T_FMT, 1,  &Nimage);
     ALLOCATE (image, RegImage, Nimage);
     status = fread (image, sizeof(RegImage), Nimage, f);
@@ -273,5 +273,5 @@
     
     if (status != Nimage) {
-      gprint (GP_ERR, "ERROR: header and data in dB don't match (%lld vs %d)\n", (long long) Nimage, status);
+      gprint (GP_ERR, "ERROR: header and data in dB don't match ("OFF_T_FMT" vs %d)\n",  Nimage, status);
       gfits_free_header (&header);
       free (image);
@@ -304,5 +304,5 @@
   /* convert to internal format */
   image = (RegImage *) table.buffer;
-  gfits_scan (table.header, "NAXIS2", "%lld", 1, (long long *) &Nimage);
+  gfits_scan (table.header, "NAXIS2", OFF_T_FMT, 1,  &Nimage);
   gfits_convert_RegImage (image, sizeof (RegImage), Nimage);
 
Index: /branches/pap/Ohana/src/opihi/dvo/imsearch.c
===================================================================
--- /branches/pap/Ohana/src/opihi/dvo/imsearch.c	(revision 28483)
+++ /branches/pap/Ohana/src/opihi/dvo/imsearch.c	(revision 28484)
@@ -106,10 +106,10 @@
 
   /* load existing data from database */
-  gfits_scan (&header, "NIMAGES", "%lld", 1, (long long *) &Nimage);
+  gfits_scan (&header, "NIMAGES", OFF_T_FMT, 1,  &Nimage);
   ALLOCATE (pimage, RegImage, Nimage);
   status = fread (pimage, sizeof(RegImage), Nimage, f);
   fclose (f);
   if (status != Nimage) {
-    gprint (GP_ERR, "ERROR: header and data in dB don't match (%lld vs %lld)\n", (long long) Nimage, (long long) status);
+    gprint (GP_ERR, "ERROR: header and data in dB don't match ("OFF_T_FMT" vs "OFF_T_FMT")\n",  Nimage,  status);
     gfits_free_header (&header);
     free (pimage);
@@ -131,5 +131,5 @@
     obstime[strlen(obstime)-1] = 0;
 
-    gprint (GP_LOG, "%5lld %6s %6s %2d %2d   ", (long long) i, get_type_name(pimage[i].type), get_mode_name(pimage[i].mode), pimage[i].ccd, pimage[i].type);
+    gprint (GP_LOG, OFF_T_FMT" %6s %6s %2d %2d   ",  i, get_type_name(pimage[i].type), get_mode_name(pimage[i].mode), pimage[i].ccd, pimage[i].type);
     gprint (GP_LOG, "%s %s  ", pimage[i].pathname, pimage[i].filename);
     gprint (GP_LOG, "%s %s %f %s\n", pimage[i].filter, pimage[i].instrument, pimage[i].exptime, obstime);
Index: /branches/pap/Ohana/src/opihi/dvo/mextract.c
===================================================================
--- /branches/pap/Ohana/src/opihi/dvo/mextract.c	(revision 28483)
+++ /branches/pap/Ohana/src/opihi/dvo/mextract.c	(revision 28484)
@@ -120,5 +120,5 @@
     catalog.Nsecfilt = Nsecfilt;
 
-    if (VERBOSE) gprint (GP_ERR, "trying %s (%lld of %lld)\n", catalog.filename, (long long) i, (long long) skylist[0].Nregions);
+    if (VERBOSE) gprint (GP_ERR, "trying %s ("OFF_T_FMT" of "OFF_T_FMT")\n", catalog.filename,  i,  skylist[0].Nregions);
       
     // an error exit status here is a significant error
Index: /branches/pap/Ohana/src/opihi/dvo/mmextract.c
===================================================================
--- /branches/pap/Ohana/src/opihi/dvo/mmextract.c	(revision 28483)
+++ /branches/pap/Ohana/src/opihi/dvo/mmextract.c	(revision 28484)
@@ -186,5 +186,5 @@
     catalog.Nsecfilt = Nsecfilt;
 
-    if (VERBOSE) gprint (GP_ERR, "trying %s (%lld of %lld)\n", catalog.filename, (long long) i, (long long) skylist[0].Nregions);
+    if (VERBOSE) gprint (GP_ERR, "trying %s ("OFF_T_FMT" of "OFF_T_FMT")\n", catalog.filename,  i,  skylist[0].Nregions);
       
     // an error exit status here is a significant error
Index: /branches/pap/Ohana/src/opihi/include/pcontrol.h
===================================================================
--- /branches/pap/Ohana/src/opihi/include/pcontrol.h	(revision 28483)
+++ /branches/pap/Ohana/src/opihi/include/pcontrol.h	(revision 28484)
@@ -120,6 +120,6 @@
   JobStat      state;
   JobStat      stack;
-  JobOutput    stdout;
-  JobOutput    stderr;
+  JobOutput    stdout_buf;
+  JobOutput    stderr_buf;
   Ptime        start;
   Ptime        stop;
Index: /branches/pap/Ohana/src/opihi/pcontrol/CheckBusyJob.c
===================================================================
--- /branches/pap/Ohana/src/opihi/pcontrol/CheckBusyJob.c	(revision 28483)
+++ /branches/pap/Ohana/src/opihi/pcontrol/CheckBusyJob.c	(revision 28484)
@@ -94,11 +94,11 @@
   sscanf (p, "%*s %d", &job[0].exit_status);
   p = memstr (buffer[0].buffer, "STDOUT", buffer[0].Nbuffer);
-  sscanf (p, "%*s %d", &job[0].stdout.size);
+  sscanf (p, "%*s %d", &job[0].stdout_buf.size);
   p = memstr (buffer[0].buffer, "STDERR", buffer[0].Nbuffer);
-  sscanf (p, "%*s %d", &job[0].stderr.size);
+  sscanf (p, "%*s %d", &job[0].stderr_buf.size);
 
   // XXX runaway job if output too large?
-  if (job[0].stdout.size > 0x1000000) abort();
-  if (job[0].stderr.size > 0x1000000) abort();
+  if (job[0].stdout_buf.size > 0x1000000) abort();
+  if (job[0].stderr_buf.size > 0x1000000) abort();
 
   // job has exited : move to DONE stack 
Index: /branches/pap/Ohana/src/opihi/pcontrol/CheckDoneJob.c
===================================================================
--- /branches/pap/Ohana/src/opihi/pcontrol/CheckDoneJob.c	(revision 28483)
+++ /branches/pap/Ohana/src/opihi/pcontrol/CheckDoneJob.c	(revision 28484)
@@ -19,10 +19,10 @@
 
   // we can always call this for stdout (if it is done, this is a NOP)
-  status1 = GetJobOutput ("stdout", host, &job[0].stdout);
+  status1 = GetJobOutput ("stdout", host, &job[0].stdout_buf);
 
   // we cannot try stderr until stdout is completed
   status2 = PCLIENT_HUNG;
-  if (job[0].stdout.completed) {
-      status2 = GetJobOutput ("stderr", host, &job[0].stderr);
+  if (job[0].stdout_buf.completed) {
+      status2 = GetJobOutput ("stderr", host, &job[0].stderr_buf);
   }
 
Index: /branches/pap/Ohana/src/opihi/pcontrol/JobOps.c
===================================================================
--- /branches/pap/Ohana/src/opihi/pcontrol/JobOps.c	(revision 28483)
+++ /branches/pap/Ohana/src/opihi/pcontrol/JobOps.c	(revision 28484)
@@ -216,6 +216,6 @@
   job[0].Reset    = FALSE;
 
-  InitJobOutput (&job[0].stdout);
-  InitJobOutput (&job[0].stderr);
+  InitJobOutput (&job[0].stdout_buf);
+  InitJobOutput (&job[0].stderr_buf);
 
   job[0].mode     = mode;
@@ -257,6 +257,6 @@
   FREE (job[0].argv);
 
-  FreeIOBuffer (&job[0].stdout.buffer);
-  FreeIOBuffer (&job[0].stderr.buffer);
+  FreeIOBuffer (&job[0].stdout_buf.buffer);
+  FreeIOBuffer (&job[0].stderr_buf.buffer);
 
   FREE (job);
Index: /branches/pap/Ohana/src/opihi/pcontrol/StartHost.c
===================================================================
--- /branches/pap/Ohana/src/opihi/pcontrol/StartHost.c	(revision 28483)
+++ /branches/pap/Ohana/src/opihi/pcontrol/StartHost.c	(revision 28484)
@@ -14,7 +14,6 @@
   if (VarConfig ("SHELL", "%s", shell)     == NULL) strcpy (shell, "pclient");
 
-#ifndef __APPLE__
-  if (VerboseMode()) gprint (GP_ERR, "starting host within thread %lld\n", (long long) pthread_self());
-#endif
+  if (VerboseMode()) gprint (GP_ERR, "starting host within thread\n");
+
   pid = rconnect (command, host[0].hostname, shell, stdio);
   if (!pid) {     
Index: /branches/pap/Ohana/src/opihi/pcontrol/StartJob.c
===================================================================
--- /branches/pap/Ohana/src/opihi/pcontrol/StartJob.c	(revision 28483)
+++ /branches/pap/Ohana/src/opihi/pcontrol/StartJob.c	(revision 28484)
@@ -13,6 +13,6 @@
   ASSERT (job  == (Job *) host[0].job, "invalid job");
 
-  ResetJobOutput (&job[0].stdout);
-  ResetJobOutput (&job[0].stderr);
+  ResetJobOutput (&job[0].stdout_buf);
+  ResetJobOutput (&job[0].stderr_buf);
 
   /* construct command line : job arg0 arg1 ... argN\n */
@@ -36,5 +36,5 @@
   }
 
-  fprintf (stderr, "command: %s\n", line);
+  // fprintf (stderr, "command: %s\n", line);
 
   status = PclientCommand (host, line, PCLIENT_PROMPT, PCONTROL_RESP_START_JOB);
Index: /branches/pap/Ohana/src/opihi/pcontrol/StopHosts.c
===================================================================
--- /branches/pap/Ohana/src/opihi/pcontrol/StopHosts.c	(revision 28483)
+++ /branches/pap/Ohana/src/opihi/pcontrol/StopHosts.c	(revision 28484)
@@ -128,7 +128,5 @@
   int i, result, waitstatus;
 
-#ifndef __APPLE__
-  if (VerboseMode()) gprint (GP_ERR, "harvesting within thread %lld\n", (long long) pthread_self());
-#endif
+  if (VerboseMode()) gprint (GP_ERR, "harvesting within thread\n");
   if (VerboseMode()) gprint (GP_ERR, "child process %d is down, wait for exit status\n", pid);
   
Index: /branches/pap/Ohana/src/opihi/pcontrol/check.c
===================================================================
--- /branches/pap/Ohana/src/opihi/pcontrol/check.c	(revision 28483)
+++ /branches/pap/Ohana/src/opihi/pcontrol/check.c	(revision 28484)
@@ -38,6 +38,6 @@
     gprint (GP_LOG, "STATUS %s\n", GetJobStackName(job[0].stack));
     gprint (GP_LOG, "EXITST %d\n", job[0].exit_status);
-    gprint (GP_LOG, "STDOUT %d\n", job[0].stdout.size);
-    gprint (GP_LOG, "STDERR %d\n", job[0].stderr.size);
+    gprint (GP_LOG, "STDOUT %d\n", job[0].stdout_buf.size);
+    gprint (GP_LOG, "STDERR %d\n", job[0].stderr_buf.size);
     gprint (GP_LOG, "DTIME %lf\n", job[0].dtime);
     if (job[0].realhost) {
@@ -50,6 +50,6 @@
 	set_str_variable ("JOB_STATUS", GetJobStackName(job[0].stack));
 	set_int_variable ("JOB_EXITST", job[0].exit_status);
-	set_int_variable ("JOB_STDOUT_SIZE", job[0].stdout.size);
-	set_int_variable ("JOB_STDERR_SIZE", job[0].stderr.size);
+	set_int_variable ("JOB_STDOUT_SIZE", job[0].stdout_buf.size);
+	set_int_variable ("JOB_STDERR_SIZE", job[0].stderr_buf.size);
 	set_variable ("JOB_DTIME", job[0].dtime);
 	set_str_variable ("JOB_HOSTNAME", job[0].hostname);
Index: /branches/pap/Ohana/src/opihi/pcontrol/stdout.c
===================================================================
--- /branches/pap/Ohana/src/opihi/pcontrol/stdout.c	(revision 28483)
+++ /branches/pap/Ohana/src/opihi/pcontrol/stdout.c	(revision 28484)
@@ -43,5 +43,5 @@
 
 found_stdout:
-  buffer = &job[0].stdout.buffer;
+  buffer = &job[0].stdout_buf.buffer;
   if (varName == NULL) {
     fwrite (buffer[0].buffer, 1, buffer[0].Nbuffer, stdout);
@@ -97,5 +97,5 @@
 
 found_stderr:
-  buffer = &job[0].stderr.buffer;
+  buffer = &job[0].stderr_buf.buffer;
   if (varName == NULL) {
     fwrite (buffer[0].buffer, 1, buffer[0].Nbuffer, stdout);
Index: /branches/pap/Ohana/src/photdbc/include/photdbc.h
===================================================================
--- /branches/pap/Ohana/src/photdbc/include/photdbc.h	(revision 28483)
+++ /branches/pap/Ohana/src/photdbc/include/photdbc.h	(revision 28484)
@@ -52,4 +52,5 @@
 double CHISQ_MAX;
 double SIGMA_MAX;
+double AVE_SIGMA_LIM;
 int    NMEAS_MIN;
 double ZERO_POINT;
Index: /branches/pap/Ohana/src/photdbc/src/ConfigInit.c
===================================================================
--- /branches/pap/Ohana/src/photdbc/src/ConfigInit.c	(revision 28483)
+++ /branches/pap/Ohana/src/photdbc/src/ConfigInit.c	(revision 28484)
@@ -42,4 +42,5 @@
 
   ScanConfig (config, "SIGMA_MAX",              "%lf", 0, &SIGMA_MAX);
+  ScanConfig (config, "AVE_SIGMA_LIM",          "%lf", 0, &AVE_SIGMA_LIM);
   ScanConfig (config, "NMEAS_MIN",              "%d",  0, &NMEAS_MIN);
 
Index: /branches/pap/Ohana/src/photdbc/src/copy_images.c
===================================================================
--- /branches/pap/Ohana/src/photdbc/src/copy_images.c	(revision 28483)
+++ /branches/pap/Ohana/src/photdbc/src/copy_images.c	(revision 28484)
@@ -3,6 +3,8 @@
 int copy_images (char *outdir) {
 
-  int status, Nimage;
+  int status;
+  off_t Nimage;
   char *ImageOut;
+  unsigned int imageID;
   FITS_DB in;
   FITS_DB out;
@@ -46,4 +48,12 @@
   dvo_image_addrows (&out, image, Nimage);
 
+  // note that imageID is unsigned int
+  status = gfits_scan (&in.header, "IMAGEID", "%u", 1, &imageID);
+  if (!status) {
+    status = gfits_scan (&in.header, "NIMAGES", "%u", 1, &imageID);
+    imageID++;
+  }
+  status = gfits_modify (&out.header, "IMAGEID", "%u", 1, imageID);
+
   dvo_image_update (&out, VERBOSE);
   dvo_image_unlock (&out);
Index: /branches/pap/Ohana/src/photdbc/src/join_stars.c
===================================================================
--- /branches/pap/Ohana/src/photdbc/src/join_stars.c	(revision 28483)
+++ /branches/pap/Ohana/src/photdbc/src/join_stars.c	(revision 28484)
@@ -7,5 +7,5 @@
   off_t Naverage, Nmeasure, *index;
   double *X, *Y, dX, dY, dR, RADIUS2;
-  double Sr, Sd, Rmid, Dmid;
+  double Rmid, Dmid;
   int basecode, baseNsec, Nsecfilt, *found;
 
@@ -202,5 +202,5 @@
 
   if (Nmeas != Nmeasure) {
-    fprintf (stderr, "failure to match %d measures (%d of %d matched)\n", Nmeasure - Nmeas, Nmeas, Nmeasure);
+    fprintf (stderr, "failure to match "OFF_T_FMT" measures ("OFF_T_FMT" of "OFF_T_FMT" matched)\n", Nmeasure - Nmeas, Nmeas, Nmeasure);
   }
   
Index: /branches/pap/Ohana/src/photdbc/src/make_subcatalog.c
===================================================================
--- /branches/pap/Ohana/src/photdbc/src/make_subcatalog.c	(revision 28483)
+++ /branches/pap/Ohana/src/photdbc/src/make_subcatalog.c	(revision 28484)
@@ -5,8 +5,9 @@
 int make_subcatalog (Catalog *subcatalog, Catalog *catalog) {
   
-  int i, j, offset;
-  int NAVERAGE, NMEASURE, Naverage, Nmeasure, Nm, Nsecfilt;
+  off_t i, j, offset;
+  off_t NAVERAGE, NMEASURE, Naverage, Nmeasure, Nm, Nsecfilt;
   double mag, minMag;
-
+  int keep;
+  
   Nsecfilt = GetPhotcodeNsecfilt ();
   assert (catalog[0].Nsecfilt == Nsecfilt);
@@ -25,4 +26,15 @@
     // exclude stars with too few measurements
     if (NMEAS_MIN && (catalog[0].average[i].Nmeasure < NMEAS_MIN)) continue; 
+
+    if (AVE_SIGMA_LIM) {
+      // if all of the average magnitude errors are >AVE_SIGMA_LIM, drop the object
+      keep = FALSE;
+      for (j = 0; !keep && (j < Nsecfilt); j++) {
+	if (catalog[0].secfilt[Nsecfilt*i+j].dM < AVE_SIGMA_LIM) {
+	  keep = TRUE;
+	}
+      }
+      if (!keep) continue;
+    }
 
     /* assign average and secfilt values */
@@ -105,5 +117,5 @@
 
   if (VERBOSE) {
-    fprintf (stderr, "%d: using %d stars (%d measures) for catalog\n", i, 
+    fprintf (stderr, OFF_T_FMT": using "OFF_T_FMT" stars ("OFF_T_FMT" measures) for catalog\n", i, 
 	     subcatalog[0].Naverage, subcatalog[0].Nmeasure);
   }
Index: /branches/pap/Ohana/src/photdbc/src/photdbc.c
===================================================================
--- /branches/pap/Ohana/src/photdbc/src/photdbc.c	(revision 28483)
+++ /branches/pap/Ohana/src/photdbc/src/photdbc.c	(revision 28484)
@@ -21,5 +21,5 @@
   skylist = SkyListByPatch (sky, -1, &REGION);
   for (i = 0; i < skylist[0].Nregions; i++) {
-    if (i % 100 == 0) fprintf (stderr, "%s\n", skylist[0].regions[i][0].name);
+    if (VERBOSE) fprintf (stderr, "%s\n", skylist[0].regions[i][0].name);
 
     // set the parameters which guide catalog open/load/create
@@ -56,9 +56,17 @@
     }
 
+    // the output catalog needs to have the same values for 'objID' and 'sorted' as the input
+    outcatalog.objID = incatalog.objID;
+    outcatalog.sorted = incatalog.sorted;
+    if (!incatalog.sorted) {
+      fprintf (stderr, "ERROR: input db must be sorted: %s\n", incatalog.filename);
+      exit (2);
+    }
+
     /* limit number of measures based on selections */
     make_subcatalog (&outcatalog, &incatalog);
 	
     // XXX add other filters here:
-    join_stars (&outcatalog);
+    // join_stars (&outcatalog);
     // unique_measures (catalog);
     // flag_measures (&db, catalog);
Index: /branches/pap/Ohana/src/relastro/Makefile
===================================================================
--- /branches/pap/Ohana/src/relastro/Makefile	(revision 28483)
+++ /branches/pap/Ohana/src/relastro/Makefile	(revision 28484)
@@ -56,6 +56,8 @@
 $(SRC)/save_catalogs.$(ARCH).o       \
 $(SRC)/write_coords.$(ARCH).o        \
-$(SRC)/CoordOps.$(ARCH).o        \
-$(SRC)/FixProblemImages.$(ARCH).o        \
+$(SRC)/CoordOps.$(ARCH).o            \
+$(SRC)/FixProblemImages.$(ARCH).o    \
+$(SRC)/high_speed_catalogs.$(ARCH).o  \
+$(SRC)/high_speed_objects.$(ARCH).o  \
 $(SRC)/relastroVisual.$(ARCH).o
 
Index: /branches/pap/Ohana/src/relastro/doc/high-speed.txt
===================================================================
--- /branches/pap/Ohana/src/relastro/doc/high-speed.txt	(revision 28484)
+++ /branches/pap/Ohana/src/relastro/doc/high-speed.txt	(revision 28484)
@@ -0,0 +1,7 @@
+
+Niall Deacon's 2MASS / PS1 high-speed search uses the following set of criteria (roughly):
+
+2 sets of detections:
+
+A : 2MASS only, J significant, H & K anything, set of phot_flags?
+B : PS1 only, 2 y-band, 2 z-band, S/N > 5, not a galaxy 
Index: /branches/pap/Ohana/src/relastro/include/relastro.h
===================================================================
--- /branches/pap/Ohana/src/relastro/include/relastro.h	(revision 28483)
+++ /branches/pap/Ohana/src/relastro/include/relastro.h	(revision 28484)
@@ -14,5 +14,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} FitTarget;
+typedef enum {TARGET_NONE, TARGET_OBJECTS, TARGET_SIMPLE, TARGET_CHIPS, TARGET_MOSAICS, TARGET_HIGH_SPEED} FitTarget;
 
 typedef struct {
@@ -89,4 +89,6 @@
 int SRC_MEAS_TOOFEW; //catalog objects wich fewer detections then this are ignored
 double MIN_ERROR;
+
+double RADIUS; // match radius for high-speed objects
 
 int    VERBOSE;
@@ -112,4 +114,8 @@
 int           NphotcodesKeep,      NphotcodesSkip;
 PhotCode     **photcodesKeep,     **photcodesSkip;
+
+char          *PHOTCODE_A_LIST,  *PHOTCODE_B_LIST;
+int           NphotcodesGroupA,  NphotcodesGroupB;
+PhotCode     **photcodesGroupA, **photcodesGroupB;
 
 int AreaSelect;
@@ -321,2 +327,6 @@
 int saveCoords (Coords *coords, off_t N);
 void resetImageRaw (Catalog *catalog, int Ncatalog, off_t im);
+
+int high_speed_catalogs ();
+int high_speed_objects (SkyRegion *region, Catalog *catalog);
+int MeasMatchesPhotcode(Measure *measure, PhotCode **photcodeSet, int Nset);
Index: /branches/pap/Ohana/src/relastro/src/ImageOps.c
===================================================================
--- /branches/pap/Ohana/src/relastro/src/ImageOps.c	(revision 28483)
+++ /branches/pap/Ohana/src/relastro/src/ImageOps.c	(revision 28484)
@@ -165,5 +165,5 @@
   for (i = 0; VERBOSE2 && (i < Nimage); i++) {
     name = GetPhotcodeNamebyCode (image[i].photcode);
-    fprintf (stderr, "image %lld has %lld measures (%s, %s)\n", (long long) i, (long long) Nlist[i],
+    fprintf (stderr, "image "OFF_T_FMT" has "OFF_T_FMT" measures (%s, %s)\n",  i,  Nlist[i],
              ohana_sec_to_date(image[i].tzero), name);
   }
Index: /branches/pap/Ohana/src/relastro/src/UpdateChips.c
===================================================================
--- /branches/pap/Ohana/src/relastro/src/UpdateChips.c	(revision 28483)
+++ /branches/pap/Ohana/src/relastro/src/UpdateChips.c	(revision 28484)
@@ -30,7 +30,7 @@
 
     // FitChip does iterative, clipped fitting
-    // fprintf (stderr, "image %lld : Nstars: %lld\n", (long long) i, (long long) Nraw);
+    // fprintf (stderr, "image "OFF_T_FMT" : Nstars: "OFF_T_FMT"\n",  i,  Nraw);
     if (!FitChip (raw, ref, Nraw, &image[i].coords)) {
-      fprintf (stderr, "reject fit for image %s (%lld) : Nstars: %lld\n", image[i].name, (long long) i, (long long) Nraw);
+      fprintf (stderr, "reject fit for image %s ("OFF_T_FMT") : Nstars: "OFF_T_FMT"\n", image[i].name,  i,  Nraw);
       oldCoords = getCoords (i);
       memcpy (&image[i].coords, oldCoords, sizeof(Coords));
Index: /branches/pap/Ohana/src/relastro/src/UpdateObjects.c
===================================================================
--- /branches/pap/Ohana/src/relastro/src/UpdateObjects.c	(revision 28483)
+++ /branches/pap/Ohana/src/relastro/src/UpdateObjects.c	(revision 28484)
@@ -80,5 +80,5 @@
   for (i = 0; i < Ncatalog; i++) {
 
-    if (VERBOSE) fprintf (stderr, "astrometrize catalog %d : %lld ave, %lld meas\n", i, (long long) catalog[i].Naverage, (long long) catalog[i].Nmeasure);
+    if (VERBOSE) fprintf (stderr, "astrometrize catalog %d : "OFF_T_FMT" ave, "OFF_T_FMT" meas\n", i,  catalog[i].Naverage,  catalog[i].Nmeasure);
 
     Nave = Npar = Npm = Nskip = 0;
@@ -189,5 +189,5 @@
 	  T[k] -= Tmean;
 	  if (XVERB) {
-	    fprintf (stderr, "%lld %f %f %f  %f %f +/- %f %f\n", (long long) k, T[k], R[k], D[k], X[k], Y[k], dX[k], dY[k]);
+	    fprintf (stderr, OFF_T_FMT" %f %f %f  %f %f +/- %f %f\n",  k, T[k], R[k], D[k], X[k], Y[k], dX[k], dY[k]);
 	  }
 	}	  
@@ -316,8 +316,8 @@
     NparSum += Npar;
     NskipSum += Nskip;
-    if (VERBOSE) fprintf (stderr, "catalog %lld : %lld ave, %lld pm, %lld par : Nskip %lld\n", (long long) i, (long long) Nave, (long long) Npm, (long long) Npar, (long long) Nskip);
+    if (VERBOSE) fprintf (stderr, "catalog %d : "OFF_T_FMT" ave, "OFF_T_FMT" pm, "OFF_T_FMT" par : Nskip "OFF_T_FMT"\n",  i,  Nave,  Npm,  Npar,  Nskip);
   }
 
-  fprintf (stderr, "fitted %lld objects (%lld ave, %lld pm, %lld par), skipped %lld\n", (long long) (NaveSum + NpmSum + NparSum), (long long) NaveSum, (long long) NpmSum, (long long) NparSum, (long long) NskipSum);
+  fprintf (stderr, "fitted "OFF_T_FMT" objects ("OFF_T_FMT" ave, "OFF_T_FMT" pm, "OFF_T_FMT" par), skipped "OFF_T_FMT"\n",  (NaveSum + NpmSum + NparSum),  NaveSum,  NpmSum,  NparSum,  NskipSum);
   return (TRUE);
 }
Index: /branches/pap/Ohana/src/relastro/src/args.c
===================================================================
--- /branches/pap/Ohana/src/relastro/src/args.c	(revision 28483)
+++ /branches/pap/Ohana/src/relastro/src/args.c	(revision 28484)
@@ -28,4 +28,17 @@
     }
   }
+  if ((N = get_argument (argc, argv, "-high-speed"))) {
+    // XXX include a parallax / no-parallax option
+    if (N >= argc - 3) usage();
+    FIT_TARGET = TARGET_HIGH_SPEED;
+    remove_argument (N, &argc, argv);
+    PHOTCODE_A_LIST = strcreate(argv[N]);
+    remove_argument (N, &argc, argv);
+    PHOTCODE_B_LIST = strcreate(argv[N]);
+    remove_argument (N, &argc, argv);
+    RADIUS = atof(argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
   if ((N = get_argument (argc, argv, "-update-simple"))) {
     remove_argument (N, &argc, argv);
@@ -271,6 +284,6 @@
 void usage () {
   fprintf (stderr, "ERROR: USAGE: relastro -region RA RA DEC DEC\n");
-  fprintf (stderr, "       OR:    relastro -catalog (ra) (dec)\n");
-  fprintf (stderr, "  working options: \n");
+  fprintf (stderr, "       OR:    relastro -catalog (ra) (dec)\n\n");
+  fprintf (stderr, "  specify one of the following modes: \n");
   fprintf (stderr, "  -update-objects\n");
   fprintf (stderr, "    -pm\n");
@@ -280,4 +293,6 @@
   fprintf (stderr, "  -update-chips\n");
   fprintf (stderr, "  -update-mosaics\n");
+  fprintf (stderr, "  -high-speed (code[,code,code]) (code[,code,code]) (radius)\n\n");
+  fprintf (stderr, "  additional options: \n");
   fprintf (stderr, "  -time (start)(stop)\n");
   fprintf (stderr, "  +photcode (code)[,code,code...]\n");
Index: /branches/pap/Ohana/src/relastro/src/bcatalog.c
===================================================================
--- /branches/pap/Ohana/src/relastro/src/bcatalog.c	(revision 28483)
+++ /branches/pap/Ohana/src/relastro/src/bcatalog.c	(revision 28484)
@@ -104,5 +104,5 @@
 
   if (VERBOSE) {
-    fprintf (stderr, "%lld: using %lld stars (%lld measures) for catalog\n", (long long) i, (long long) subcatalog[0].Naverage, (long long) subcatalog[0].Nmeasure);
+    fprintf (stderr, OFF_T_FMT": using "OFF_T_FMT" stars ("OFF_T_FMT" measures) for catalog\n",  i,  subcatalog[0].Naverage,  subcatalog[0].Nmeasure);
    }
   return (TRUE);
Index: /branches/pap/Ohana/src/relastro/src/high_speed_catalogs.c
===================================================================
--- /branches/pap/Ohana/src/relastro/src/high_speed_catalogs.c	(revision 28484)
+++ /branches/pap/Ohana/src/relastro/src/high_speed_catalogs.c	(revision 28484)
@@ -0,0 +1,55 @@
+# include "relastro.h"
+
+int high_speed_catalogs () {
+
+  int i;
+
+  SkyTable *sky = NULL;
+  SkyList *skylist = NULL;
+  Catalog catalog;
+
+  // load the current sky table (layout of all SkyRegions) 
+  sky = SkyTableLoadOptimal (CATDIR, SKY_TABLE, GSCFILE, TRUE, SKY_DEPTH, VERBOSE);
+  SkyTableSetFilenames (sky, CATDIR, "cpt");
+  
+  // determine the populated SkyRegions overlapping the requested area (default depth)
+  if (UserCatalog) {
+    skylist = SkyRegionByPoint (sky, -1, UserCatalogRA, UserCatalogDEC);
+  } else {
+    skylist = SkyListByPatch (sky, -1, &UserPatch);
+  }
+
+  // load data from each region file, only use bright stars
+  for (i = 0; i < skylist[0].Nregions; i++) {
+
+    // set up the basic catalog info
+    catalog.filename  = skylist[0].filename[i];
+    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 ();
+
+    if (!dvo_catalog_open (&catalog, skylist[0].regions[i], VERBOSE, "w")) {
+      fprintf (stderr, "ERROR: failure reading catalog %s\n", catalog.filename);
+      exit (1);
+    }
+    if (!catalog.Naves_disk) {
+      if (VERBOSE) fprintf (stderr, "no data in %s, skipping\n", catalog.filename);
+      dvo_catalog_unlock (&catalog);
+      dvo_catalog_free (&catalog);
+      continue;
+    }
+
+    high_speed_objects (skylist[0].regions[i], &catalog);
+
+    if (!UPDATE) {
+      dvo_catalog_unlock (&catalog);
+      dvo_catalog_free (&catalog);
+      continue;
+    }
+    
+    save_catalogs (&catalog, 1);
+  }
+  
+  return (TRUE);
+}
Index: /branches/pap/Ohana/src/relastro/src/high_speed_objects.c
===================================================================
--- /branches/pap/Ohana/src/relastro/src/high_speed_objects.c	(revision 28484)
+++ /branches/pap/Ohana/src/relastro/src/high_speed_objects.c	(revision 28484)
@@ -0,0 +1,338 @@
+# include "relastro.h"
+
+# define NEXT_I { i++; continue; }
+# define NEXT_J { j++; continue; }
+
+// XXX these were used to spot-check specific objects
+# define REF_RA1 192.455781 
+# define REF_DEC1 2.563102 
+# define REF_RA2 192.444302113 
+# define REF_DEC2 2.5786559112 
+int high_speed_objects (SkyRegion *region, Catalog *catalog) {
+
+  // XXX seems silly to require region here just to find the catalog bounds / center
+
+  off_t i, j, m, J, ni, nj, *N1, Nslow, NgroupA, NgroupB;
+  int *slowMoving, *groupA, *groupB, status, foundA, foundB, Nmatch, valid;
+  double *X1, *Y1;
+  double dX, dY, dR, RADIUS2, yJ;
+  Coords tcoords;
+
+  int zcode, zNsec, ycode, yNsec, jcode, jNsec, hcode, hNsec, kcode, kNsec, USNO_R, USNO_N, Nsecfilt;
+
+  Nsecfilt = GetPhotcodeNsecfilt();
+
+  ycode = GetPhotcodeCodebyName("y");
+  yNsec = GetPhotcodeNsec(ycode);
+  
+  zcode = GetPhotcodeCodebyName("z");
+  zNsec = GetPhotcodeNsec(zcode);
+  
+  jcode = GetPhotcodeCodebyName("J");
+  jNsec = GetPhotcodeNsec(jcode);
+  
+  hcode = GetPhotcodeCodebyName("H");
+  hNsec = GetPhotcodeNsec(hcode);
+  
+  kcode = GetPhotcodeCodebyName("K");
+  kNsec = GetPhotcodeNsec(kcode);
+  
+  USNO_R = GetPhotcodeCodebyName("USNO_RED");
+  USNO_N = GetPhotcodeCodebyName("USNO_N");
+
+  // high-speed between different surveys (easier case):
+  // we have two sets of photcodes (A) and (B) and are looking for objects
+  // with detections in only (A) and separately only (B).
+
+  // we need at least 2 objects if we are going to match anything...
+  if (catalog[0].Naverage < 2) return (TRUE);
+
+  // mask with which to mark objects to be ignored
+  ALLOCATE (slowMoving, int, catalog[0].Naverage);
+  memset (slowMoving, 0, catalog[0].Naverage*sizeof(int));
+
+  // record to which photcode group the object belongs:
+  NgroupA = 0;
+  ALLOCATE (groupA, int, catalog[0].Naverage);
+  memset (groupA, 0, catalog[0].Naverage*sizeof(int));
+
+  NgroupB = 0;
+  ALLOCATE (groupB, int, catalog[0].Naverage);
+  memset (groupB, 0, catalog[0].Naverage*sizeof(int));
+
+  fprintf (stderr, "checking "OFF_T_FMT" objects\n",  catalog[0].Naverage);
+  Nslow = 0;
+
+  fprintf (stdout, "#      RA_A  DEC_A              RA_B  DEC_B      :     pmx     pmy      dR  :    z      dz       y      dy       J      dJ       H      dH       K      dK\n");
+  //................270.2346670  20.2471540  270.2170434  20.2717396 :  -59.11   88.78   106.66 :   0.000  0.000   19.582  0.112   16.041  0.110   15.388  0.098   14.858  0.001
+
+  // mark (exclude) objects with both sets of target photcodes
+  for (i = 0; i < catalog[0].Naverage; i++) {
+
+    // if (i % 100 == 0) fprintf (stderr, ".");
+
+    // radius = hypot((REF_DEC1 - catalog[0].average[i].D), (REF_RA1 - catalog[0].average[i].R));
+    // if (radius < 0.0001) {
+    //   fprintf (stderr, "found it\n");
+    // }
+    // radius = hypot((REF_DEC2 - catalog[0].average[i].D), (REF_RA2 - catalog[0].average[i].R));
+    // if (radius < 0.0001) {
+    //   fprintf (stderr, "found it\n");
+    // }
+
+    // do any of the measures for this object match group A?
+    m = catalog[0].average[i].measureOffset;
+    foundA = FALSE;
+    for (j = 0; !foundA && (j < catalog[0].average[i].Nmeasure); j++, m++) {
+      if (MeasMatchesPhotcode(&catalog[0].measure[m], photcodesGroupA, NphotcodesGroupA)) {
+	foundA = TRUE;
+      }
+    }
+
+    // do any of the measures for this object match group B?
+    m = catalog[0].average[i].measureOffset;
+    foundB = FALSE;
+    for (j = 0; !foundB && (j < catalog[0].average[i].Nmeasure); j++, m++) {
+      if (MeasMatchesPhotcode(&catalog[0].measure[m], photcodesGroupB, NphotcodesGroupB)) {
+	foundB = TRUE;
+      }
+    }
+
+    if (foundA && foundB) {
+      slowMoving[i] = TRUE;
+      Nslow ++;
+      continue;
+    }
+
+    // additional constraints:
+    // * group A : require 
+
+    // 2MASS detections:
+    if (foundA && !foundB) {
+      // average-based tests:
+      valid = TRUE;
+      valid &= ((catalog[0].average[i].flags & 0x40000) == 0);
+      valid &= ((catalog[0].average[i].flags & 0x80000)  > 0);
+      valid &= (catalog[0].secfilt[i*Nsecfilt + yNsec].M < 1.0);
+      valid &= (catalog[0].secfilt[i*Nsecfilt + zNsec].M < 1.0);
+      // XXX color restrictions are applied in the pair-wise matching below
+      
+      // measure-based tests:
+      m = catalog[0].average[i].measureOffset;
+      for (j = 0; valid && (j < catalog[0].average[i].Nmeasure); j++, m++) {
+	if (catalog[0].measure[m].photcode == USNO_R) {
+	  valid &= ((catalog[0].measure[m].M > 20.0) || isnan(catalog[0].measure[m].M));
+	}
+	if (catalog[0].measure[m].photcode == USNO_N) {
+	  valid &= ((catalog[0].measure[m].M > 18.0) || isnan(catalog[0].measure[m].M));
+	}
+      }
+
+      // ((objflags & 524288) == 524288) && 
+      // ((objflags & 262144) == 0) && 
+      // (z<1.0) && 
+      // (y<1.0) && 
+      // ((yp[$i]-J)>1.25) && ((yp[$i]-J)<5.0) && 
+      // ((USNO_RED<1.0)||(USNO_RED>20.0)) && 
+      // ((USNO_N>18.0)||(USNO_N<1.0))
+
+      if (valid) {
+	groupA[i] = TRUE;
+	NgroupA ++;
+	continue;
+      } else {
+	// fprintf (stderr, "skip "OFF_T_FMT" (group A)\n",  i);
+      }
+    }
+
+    // PS1 detections:
+    if (foundB && !foundA) {
+
+      // average-based tests:
+      valid = TRUE;
+      valid &= ((catalog[0].average[i].flags & 0x10000) == 0);
+      valid &= ((catalog[0].average[i].flags & 0x20000)  > 0);
+      valid &= (catalog[0].secfilt[i*Nsecfilt + jNsec].M < 1.0);
+      valid &= (catalog[0].secfilt[i*Nsecfilt + yNsec].Nused > 1);
+      valid &= (catalog[0].secfilt[i*Nsecfilt + yNsec].dM < 0.2);
+
+      if ((catalog[0].secfilt[i*Nsecfilt + zNsec].M < 1.0) || (catalog[0].secfilt[i*Nsecfilt + zNsec].Nused == 1)) {
+	valid &= TRUE;
+      } else {
+	valid &= ((catalog[0].secfilt[i*Nsecfilt + zNsec].M - catalog[0].secfilt[i*Nsecfilt + yNsec].M) > 0.2);
+      }
+
+      // measure-based tests:
+      m = catalog[0].average[i].measureOffset;
+      for (j = 0; valid && (j < catalog[0].average[i].Nmeasure); j++, m++) {
+	if (catalog[0].measure[m].photcode == USNO_R) {
+	  valid &= ((catalog[0].measure[m].M > 20.0) || isnan(catalog[0].measure[m].M));
+	}
+	if (catalog[0].measure[m].photcode == USNO_N) {
+	  valid &= ((catalog[0].measure[m].M > 18.5) || isnan(catalog[0].measure[m].M));
+	}
+      }
+
+      // (abs(glat)>15.0) && 
+      // ((objflags & 65536) == 0) && 
+      // ((objflags & 131072) == 131072) &&
+      // (J<1.0) && 
+      // (y:nphot>1) && 
+      // (y:err<0.2) && 
+      // ((USNO_RED>20.0)||(USNO_RED<1.0)) && 
+      // ((USNO_N>18.5)||(USNO_N<1.0)) && 
+      // ((z<1.0)||(z:nphot == 1)||((z:nphot>1)&&((z-y)>0.2)))
+
+      if (valid) {
+	groupB[i] = TRUE;
+	NgroupB ++;
+	continue;
+      } else {
+	// fprintf (stderr, "skip "OFF_T_FMT" (group B)\n",  i);
+      }
+    }
+
+    // this object does not have a detection from either groupA or groupB -- skip as if slow
+    slowMoving[i] = TRUE;
+    Nslow ++;
+  }
+  fprintf (stderr, OFF_T_FMT" of "OFF_T_FMT" are slow or invalid; "OFF_T_FMT" in group A, "OFF_T_FMT" in group B\n",  Nslow,  catalog[0].Naverage,  NgroupA,  NgroupB);
+    
+  // double loop over unmarked objects (sorted in RA / X)
+  // record all pairs within the desired match distance
+
+  // create tmp local positional index
+  ALLOCATE (X1, double, catalog[0].Naverage);
+  ALLOCATE (Y1, double, catalog[0].Naverage);
+  ALLOCATE (N1, off_t,  catalog[0].Naverage);
+
+  // define a local projection
+  tcoords.crval1 = 0.5*(region[0].Rmin + region[0].Rmax);
+  if (region[0].Dmax < 90) {
+    tcoords.crval2 = 0.5*(region[0].Dmin + region[0].Dmax);
+  } else {
+    tcoords.crval2 = 90.0;
+  }
+  tcoords.crpix1 = 0;
+  tcoords.crpix2 = 0;
+  tcoords.cdelt1 = tcoords.cdelt2 = 1.0 / 3600.0;
+  tcoords.pc1_1 = tcoords.pc2_2 = 1.0;
+  tcoords.pc1_2 = tcoords.pc2_1 = 0.0;
+  tcoords.Npolyterms = 1;
+  strcpy (tcoords.ctype, "RA---ARC");
+
+  /* build spatial index (RA sort) referencing input array sequence */
+  for (i = 0; i < catalog[0].Naverage; i++) {
+    status = RD_to_XY (&X1[i], &Y1[i], catalog[0].average[i].R, catalog[0].average[i].D, &tcoords);
+    N1[i] = i;
+    assert (status);
+  }
+  sort_coords_index (X1, Y1, N1, catalog[0].Naverage);
+
+  Nmatch = 0;
+  RADIUS2 = SQ(RADIUS);
+
+  // mark (exclude) objects with both sets of target photcodes
+  for (i = j = 0; (i < catalog[0].Naverage) && (j < catalog[0].Naverage);) {
+
+    ni = N1[i];
+    nj = N1[j];
+
+    // if (ni == 131030) {
+    //   fprintf (stderr, "got 2mass\n");
+    // }
+    // if (nj == 53581) {
+    //   fprintf (stderr, "got ps1\n");
+    // }
+
+    if (slowMoving[ni]) NEXT_I;
+    if (slowMoving[nj]) NEXT_J;
+
+    // i => groupA, j => groupB
+    if (!groupA[ni]) NEXT_I;
+    if (!groupB[nj]) NEXT_J;
+
+    if (!finite(X1[i]) || !finite(Y1[i])) NEXT_I;
+    if (!finite(X1[j]) || !finite(Y1[j])) NEXT_J;
+
+    // look for pairs that are within the maximum separation
+    dX = X1[i] - X1[j];
+
+    if (dX <= -1.02*RADIUS) NEXT_I; // negative dX: i is too small
+    if (dX >= +1.02*RADIUS) NEXT_J; // positive dX: j is too small
+
+    // within match range; look for valid matches
+    for (J = j; (dX > -1.02*RADIUS) && (J < catalog[0].Naverage); J++) {
+      if (J == i) continue;  // avoid auto-matches
+
+      dX = X1[i] - X1[J];
+
+      nj = N1[J];
+      if (!groupB[nj]) continue;
+
+      dY = Y1[i] - Y1[J];
+      dR = dX*dX + dY*dY;
+      if (dR > RADIUS2) continue;
+
+      // y_groupA - J_groupB 
+      yJ = catalog[0].secfilt[nj*Nsecfilt + yNsec].M - catalog[0].secfilt[ni*Nsecfilt + jNsec].M;
+      if (yJ < 1.25) continue;
+      if (yJ > 5.0) continue;
+
+      /*** a match is found (just print it for the moment) ***/
+      Nmatch ++;
+
+      // for the moment, just write the matches to stdout:
+      float pmx = -dX; // proper-motion displacement in ra  direction (B - A) [arcsec]
+      float pmy = -dY; // proper-motion displacement in dec direction (B - A) [arcsec]
+      fprintf (stdout, "%11.7f %11.7f  %11.7f %11.7f : %7.2f %7.2f  %7.2f : %7.3f %6.3f  %7.3f %6.3f  %7.3f %6.3f  %7.3f %6.3f  %7.3f %6.3f\n", 
+	       catalog[0].average[ni].R, catalog[0].average[ni].D,
+	       catalog[0].average[nj].R, catalog[0].average[nj].D,
+	       pmx, pmy, sqrt(dR),
+	       catalog[0].secfilt[nj*Nsecfilt + zNsec].M,
+	       catalog[0].secfilt[nj*Nsecfilt + zNsec].dM,
+	       catalog[0].secfilt[nj*Nsecfilt + yNsec].M, 
+	       catalog[0].secfilt[nj*Nsecfilt + yNsec].dM,
+	       catalog[0].secfilt[ni*Nsecfilt + jNsec].M,
+	       catalog[0].secfilt[ni*Nsecfilt + jNsec].dM,
+	       catalog[0].secfilt[ni*Nsecfilt + hNsec].M,
+	       catalog[0].secfilt[ni*Nsecfilt + hNsec].dM,
+	       catalog[0].secfilt[ni*Nsecfilt + kNsec].M,
+	       catalog[0].secfilt[ni*Nsecfilt + kNsec].dM);
+    }
+    i++;
+  }
+  fprintf (stderr, "found %d matches\n", Nmatch);
+  
+  free (slowMoving);
+  free (groupA);
+  free (groupB);
+  free (X1);
+  free (Y1);
+  free (N1);
+
+  return TRUE;
+}
+
+// return TRUE if any measure->photcodes match the photcodeSet
+int MeasMatchesPhotcode(Measure *measure, PhotCode **photcodeSet, int Nset) {
+
+  int found, k;
+
+  assert (Nset > 0);
+
+  if (!finite(measure[0].dR) || !finite(measure[0].dD)) return FALSE;
+  if (!finite(measure[0].M)) return FALSE;
+  
+  /* select measurements by photcode, or equiv photcode, if specified */
+  if (Nset > 0) {
+    found = FALSE;
+    for (k = 0; (k < Nset) && !found; k++) {
+      if (photcodeSet[k][0].code == measure[0].photcode) found = TRUE;
+      if (photcodeSet[k][0].code == GetPhotcodeEquivCodebyCode(measure[0].photcode)) found = TRUE;
+    }
+    if (!found) return FALSE;
+  }
+  
+  return TRUE;
+}
Index: /branches/pap/Ohana/src/relastro/src/initialize.c
===================================================================
--- /branches/pap/Ohana/src/relastro/src/initialize.c	(revision 28483)
+++ /branches/pap/Ohana/src/relastro/src/initialize.c	(revision 28484)
@@ -56,4 +56,46 @@
   }
 
+  NphotcodesGroupA = 0;
+  photcodesGroupA = NULL;
+  if (PHOTCODE_A_LIST != NULL) {
+    NPHOTCODES = 10;
+    ALLOCATE (photcodesGroupA, PhotCode *, NPHOTCODES);
+
+    /* parse the comma-separated list of photcodesGroupA */
+    list = PHOTCODE_A_LIST;
+    while ((codename = strtok_r (list, ",", &ptr)) != NULL) {
+      list = NULL; // pass NULL on successive strtok_r calls
+      fprintf (stderr, "PHOTCODE_A_LIST: %s\n", PHOTCODE_A_LIST);
+      fprintf (stderr, "codename: %s\n", codename);
+      if ((photcodesGroupA[NphotcodesGroupA] = GetPhotcodebyName (codename)) == NULL) {
+        fprintf (stderr, "ERROR: photcode %s not found in photcode table\n", codename);
+        exit (1);
+      }
+      NphotcodesGroupA ++;
+      CHECK_REALLOCATE (photcodesGroupA, PhotCode *, NPHOTCODES, NphotcodesGroupA, 10);
+    }
+  }
+
+  NphotcodesGroupB = 0;
+  photcodesGroupB = NULL;
+  if (PHOTCODE_B_LIST != NULL) {
+    NPHOTCODES = 10;
+    ALLOCATE (photcodesGroupB, PhotCode *, NPHOTCODES);
+
+    /* parse the comma-separated list of photcodesGroupB */
+    list = PHOTCODE_B_LIST;
+    while ((codename = strtok_r (list, ",", &ptr)) != NULL) {
+      list = NULL; // pass NULL on successive strtok_r calls
+      fprintf (stderr, "PHOTCODE_B_LIST: %s\n", PHOTCODE_B_LIST);
+      fprintf (stderr, "codename: %s\n", codename);
+      if ((photcodesGroupB[NphotcodesGroupB] = GetPhotcodebyName (codename)) == NULL) {
+        fprintf (stderr, "ERROR: photcode %s not found in photcode table\n", codename);
+        exit (1);
+      }
+      NphotcodesGroupB ++;
+      CHECK_REALLOCATE (photcodesGroupB, PhotCode *, NPHOTCODES, NphotcodesGroupB, 10);
+    }
+  }
+
   initstats (STATMODE);
 
Index: /branches/pap/Ohana/src/relastro/src/relastro.c
===================================================================
--- /branches/pap/Ohana/src/relastro/src/relastro.c	(revision 28483)
+++ /branches/pap/Ohana/src/relastro/src/relastro.c	(revision 28484)
@@ -23,4 +23,10 @@
   if (FIT_TARGET == TARGET_OBJECTS) {
     relastro_objects ();
+    exit (0);
+  }
+
+  /* the object analysis is a separate process iterating over catalogs */
+  if (FIT_TARGET == TARGET_HIGH_SPEED) {
+    high_speed_catalogs ();
     exit (0);
   }
Index: /branches/pap/Ohana/src/relastro/src/select_images.c
===================================================================
--- /branches/pap/Ohana/src/relastro/src/select_images.c	(revision 28483)
+++ /branches/pap/Ohana/src/relastro/src/select_images.c	(revision 28484)
@@ -115,5 +115,5 @@
     
     if (!FindMosaicForImage (timage, Ntimage, i)) {
-      fprintf (stderr, "cannot find mosaic for %lld\n", (long long) i);
+      fprintf (stderr, "cannot find mosaic for "OFF_T_FMT"\n",  i);
       continue;
     }
@@ -190,5 +190,5 @@
   }
       
-  if (VERBOSE) fprintf (stderr, "found %lld images\n", (long long) nimage);
+  if (VERBOSE) fprintf (stderr, "found "OFF_T_FMT" images\n",  nimage);
 
   REALLOCATE (image, Image, MAX (nimage, 1));
Index: /branches/pap/Ohana/src/relphot/src/GridOps.c
===================================================================
--- /branches/pap/Ohana/src/relphot/src/GridOps.c	(revision 28483)
+++ /branches/pap/Ohana/src/relphot/src/GridOps.c	(revision 28484)
@@ -703,5 +703,5 @@
   gfits_create_header (&header);
   gfits_create_matrix (&header, &matrix);
-  gfits_modify (&header, "NEXTEND", "%lld", 1, (long long) Nimage + 3);
+  gfits_modify (&header, "NEXTEND", OFF_T_FMT, 1,  Nimage + 3);
   gfits_modify (&header, "FILTER", "%s", 1, photcode[0].name);
   gfits_modify_alt (&header, "COMMENT", "%S", 1, "Mosaic Photometry Grid Analysis");
Index: /branches/pap/Ohana/src/relphot/src/ImageOps.c
===================================================================
--- /branches/pap/Ohana/src/relphot/src/ImageOps.c	(revision 28483)
+++ /branches/pap/Ohana/src/relphot/src/ImageOps.c	(revision 28484)
@@ -200,5 +200,5 @@
     status = findCCD (idx, meas, cat, measure);
     if (!status) {
-      if (VERBOSE2) fprintf (stderr, "failed to determine CCD for %lld, %d\n", (long long) meas, cat);
+      if (VERBOSE2) fprintf (stderr, "failed to determine CCD for "OFF_T_FMT", %d\n",  meas, cat);
       return;
     }
Index: /branches/pap/Ohana/src/relphot/src/MosaicOps.c
===================================================================
--- /branches/pap/Ohana/src/relphot/src/MosaicOps.c	(revision 28483)
+++ /branches/pap/Ohana/src/relphot/src/MosaicOps.c	(revision 28484)
@@ -400,5 +400,5 @@
       mark = (N < IMAGE_TOOFEW) || (N < IMAGE_GOOD_FRACTION*Nlist[i]);
       if (mark) {
-	fprintf (stderr, "marked image %s (%lld), (%lld < %d) || (%lld < %f*%lld)\n", image[imlist[i][0]].name, (long long) i, (long long) N, IMAGE_TOOFEW, (long long) N, IMAGE_GOOD_FRACTION, (long long) Nlist[i]);
+	fprintf (stderr, "marked mosaic %s ("OFF_T_FMT"), ("OFF_T_FMT" < %d) || ("OFF_T_FMT" < %f*"OFF_T_FMT")\n", image[imlist[i][0]].name,  i,  N, IMAGE_TOOFEW,  N, IMAGE_GOOD_FRACTION,  Nlist[i]);
 	mosaic[i].flags |= ID_IMAGE_FEW;
 	Nfew ++;
@@ -408,5 +408,5 @@
     }
     liststats (list, dlist, N, &stats);
-    if (PoorImages) fprintf (stderr, "Mmos: %f %f %d %lld\n", stats.mean, stats.sigma, stats.Nmeas, (long long) N);
+    if (PoorImages) fprintf (stderr, "Mmos: %f %f %d "OFF_T_FMT"\n", stats.mean, stats.sigma, stats.Nmeas,  N);
     mosaic[i].Mcal  = stats.mean;
     mosaic[i].dMcal = stats.sigma;
@@ -519,5 +519,5 @@
     n++;
   }
-  fprintf (stderr, "Nmosaic: %lld, n: %lld\n", (long long) Nmosaic, (long long) n);
+  fprintf (stderr, "Nmosaic: "OFF_T_FMT", n: "OFF_T_FMT"\n",  Nmosaic,  n);
 
   liststats (list, dlist, n, &stats);
@@ -599,5 +599,5 @@
   }
 
-  fprintf (stderr, "%lld mosaics marked poor\n", (long long) Nmark);
+  fprintf (stderr, OFF_T_FMT" mosaics marked poor\n",  Nmark);
   initstats (STATMODE);
   free (mlist);
@@ -643,5 +643,5 @@
     }
   
-    sprintf (string, "Mosaic %lld", (long long) i);
+    sprintf (string, "Mosaic "OFF_T_FMT,  i);
     plot_defaults (&graphdata);
     plot_list (&graphdata, xlist, ylist, N, string, NULL);
Index: /branches/pap/Ohana/src/relphot/src/StarOps.c
===================================================================
--- /branches/pap/Ohana/src/relphot/src/StarOps.c	(revision 28483)
+++ /branches/pap/Ohana/src/relphot/src/StarOps.c	(revision 28484)
@@ -391,4 +391,6 @@
 }
 
+# define NSIGMA_CLIP 3.0
+# define NSIGMA_REJECT 5.0
 void clean_measures (Catalog *catalog, int Ncatalog, int final) {
 
@@ -396,7 +398,8 @@
   int i, N, image_bad, TOOFEW;
   off_t *ilist;
-  double *tlist, *list, *dlist, Ns;
+  double *tlist, *list, *dlist;
   float Msys, Mcal, Mmos, Mgrid;
   StatType stats;
+  int Ncal, Nmos, Ngrid, Nfew;
 
   if (VERBOSE) fprintf (stderr, "marking poor measures\n");
@@ -416,6 +419,6 @@
   TOOFEW = MAX (5, STAR_TOOFEW);
 
-  Ns = 3;
   Ndel = Nave = 0;
+  Ncal = Nmos = Ngrid = Nfew = 0;
   for (i = 0; i < Ncatalog; i++) {
     for (j = 0; j < catalog[i].Naverage; j++) {
@@ -433,9 +436,9 @@
 	/* if (catalog[i].measure[m].dbFlags & MEAS_BAD) continue; */
 	Mcal  = getMcal  (m, i);
-	if (isnan(Mcal)) continue;
+	if (isnan(Mcal)) { Ncal ++; continue; }
 	Mmos  = getMmos  (m, i);
-	if (isnan(Mmos)) continue;
+	if (isnan(Mmos)) { Nmos ++; continue; }
 	Mgrid = getMgrid (m, i);
-	if (isnan(Mgrid)) continue;
+	if (isnan(Mgrid)) { Ngrid ++; continue; }
 
 	Msys = PhotSys (&catalog[i].measure[m], &catalog[i].average[j], &catalog[i].secfilt[j*PhotNsec]);
@@ -444,16 +447,21 @@
 	N++;
       }
-      if (N <= TOOFEW) continue;
+      if (N <= TOOFEW) { Nfew ++; continue; }
 
       /* 3-sigma clip based on stats of inner 50% */
+
+      // calculated mean of inner 50%
       initstats ("INNER_MEAN");
       liststats (list, dlist, N, &stats);
       stats.sigma = MAX (MIN_ERROR, stats.sigma); /* if measurements agree too well, sigma -> 0.0 */
+
+      // ignore entries > 3sigma from inner mean
       for (k = m = 0; k < N; k++) {
-	if (fabs (list[k] - stats.median) < Ns*stats.sigma) {
+	if (fabs (list[k] - stats.median) < NSIGMA_CLIP*stats.sigma) {
 	  list[m] = list[k];
 	  m++;
 	}
       }
+      // recalculate the mean & sigma of the accepted measurements
       initstats ("MEAN");
       liststats (list, dlist, m, &stats);
@@ -483,7 +491,7 @@
       if (N < TOOFEW) continue;
 
-      /* mark bad measures */
+      /* mark bad measures (> 3 sigma deviant) */
       for (k = 0; k < N; k++) {
-	if (fabs (list[k] - stats.median) > Ns*stats.sigma) {
+	if (fabs (list[k] - stats.median) > NSIGMA_REJECT*stats.sigma) {
 	  catalog[i].measure[ilist[k]].dbFlags |= ID_MEAS_POOR_PHOTOM;
 	  Ndel ++;
@@ -494,5 +502,5 @@
   }
   initstats (STATMODE);
-  if (VERBOSE) fprintf (stderr, "%lld measures marked poor, %lld total\n", (long long) Ndel, (long long) Nave);
+  if (VERBOSE) fprintf (stderr, OFF_T_FMT" measures marked poor, "OFF_T_FMT" total\n", Ndel, Nave);
   free (ilist);
   free (tlist);
Index: /branches/pap/Ohana/src/relphot/src/bcatalog.c
===================================================================
--- /branches/pap/Ohana/src/relphot/src/bcatalog.c	(revision 28483)
+++ /branches/pap/Ohana/src/relphot/src/bcatalog.c	(revision 28484)
@@ -7,4 +7,5 @@
   off_t NAVERAGE, NMEASURE, Naverage, Nmeasure, Nm;
   float mag;
+  int Ncode, Ntime, Ndophot, Nmag, Nsigma, Nimag, Nfew;
 
   // XXX PhotNsec as a global is a bad idea; either get it from catalog
@@ -20,4 +21,6 @@
   ALLOCATE (subcatalog[0].measure, Measure, NMEASURE);
   Nmeasure = Naverage = 0;
+
+  Ncode = Ntime = Ndophot = Nmag = Nsigma = Nimag = Nfew = 0;
 
   /* exclude stars not in range or with too few measurements */
@@ -46,10 +49,10 @@
       /* select measurements by photcode */
       ecode = GetPhotcodeEquivCodebyCode (catalog[0].measure[offset].photcode);
-      if (ecode != photcode[0].code) continue;
+      if (ecode != photcode[0].code) { Ncode ++; continue; }
 
       /* select measurements by time */
       if (TimeSelect) {
-	if (catalog[0].measure[offset].t < TSTART) continue;
-	if (catalog[0].measure[offset].t > TSTOP) continue;
+	if (catalog[0].measure[offset].t < TSTART) { Ntime ++; continue; }
+	if (catalog[0].measure[offset].t > TSTOP)  { Ntime ++; continue; }
       }
 
@@ -57,18 +60,18 @@
       // XXX ignore this criterion for REF measurements?
       // XXX chnage this to select by bitflags
-      if (DophotSelect && ((catalog[0].measure[offset].photFlags >> 16) != DophotValue)) continue;
+      if (DophotSelect && ((catalog[0].measure[offset].photFlags >> 16) != DophotValue)) { Ndophot ++; continue; }
 
       /* select measurements by mag limit */
       mag = PhotCat (&catalog[0].measure[offset]);
-      if (mag > MAG_LIM) continue;
+      if (mag > MAG_LIM) { Nmag ++; continue; } 
 
       /* select measurements by measurement error */
-      if ((SIGMA_LIM > 0) && (catalog[0].measure[offset].dM > SIGMA_LIM)) continue;
+      if ((SIGMA_LIM > 0) && (catalog[0].measure[offset].dM > SIGMA_LIM)) { Nsigma ++; continue; }
 
       /* select measurements by mag limit */
       if (ImagSelect) {
 	mag = PhotInst (&catalog[0].measure[offset]);
-	if (mag < ImagMin) continue;
-	if (mag > ImagMax) continue;
+	if (mag < ImagMin) { Nimag ++; continue; }
+	if (mag > ImagMax) { Nimag ++; continue; }
       }
 
@@ -94,4 +97,5 @@
     if (Nm <= STAR_TOOFEW) { /* enough measurements in band? */
       Nmeasure -= Nm;
+      Nfew ++;
       continue; 
     }
@@ -114,6 +118,8 @@
 
   if (VERBOSE) {
-    fprintf (stderr, "%lld: using %lld stars (%lld measures) for catalog\n", (long long) i, 
-	     (long long) subcatalog[0].Naverage, (long long) subcatalog[0].Nmeasure);
+    fprintf (stderr, "using "OFF_T_FMT" stars ("OFF_T_FMT" measures) of "OFF_T_FMT" for catalog\n", 
+	      subcatalog[0].Naverage,  subcatalog[0].Nmeasure,  i);
+    fprintf (stderr, "rejections: %d code, %d time, %d dophot, %d mag, %d sigma, %d imag, %d few\n", 
+	     Ncode, Ntime, Ndophot, Nmag, Nsigma, Nimag, Nfew);
   }
   return (TRUE);
Index: /branches/pap/Ohana/src/relphot/src/load_catalogs.c
===================================================================
--- /branches/pap/Ohana/src/relphot/src/load_catalogs.c	(revision 28483)
+++ /branches/pap/Ohana/src/relphot/src/load_catalogs.c	(revision 28484)
@@ -47,4 +47,7 @@
 
   fprintf (stderr, "using %d of %d stars (%d of %d measurements)\n", Nstar, Nstar_total, Nmeas, Nmeas_total);
+  if (Nstar < 1) {
+      Shutdown ("ERROR: no stars match the minimum requirements; exiting\n");
+  }
 
   // XXX consider only returning the populated catalogs
Index: /branches/pap/Ohana/src/relphot/src/load_images.c
===================================================================
--- /branches/pap/Ohana/src/relphot/src/load_images.c	(revision 28483)
+++ /branches/pap/Ohana/src/relphot/src/load_images.c	(revision 28484)
@@ -47,5 +47,5 @@
   vtable = &db[0].vtable;
 
-  gfits_scan (vtable[0].header, "NAXIS1", "%lld", 1, (long long *) &Nx);
+  gfits_scan (vtable[0].header, "NAXIS1", OFF_T_FMT, 1,  &Nx);
   for (i = 0; i < Nimage; i++) {
     memcpy (vtable[0].buffer[i], &image[i], Nx);
Index: /branches/pap/Ohana/src/relphot/src/relphot_objects.c
===================================================================
--- /branches/pap/Ohana/src/relphot/src/relphot_objects.c	(revision 28483)
+++ /branches/pap/Ohana/src/relphot/src/relphot_objects.c	(revision 28484)
@@ -55,4 +55,5 @@
 	for (k = 0; k < catalog.average[j].Nmeasure; k++) {
 	  catalog.measure[m+k].dbFlags = 0;
+	  catalog.measure[m+k].Mcal = 0;
 	}
       }
Index: /branches/pap/Ohana/src/relphot/src/select_images.c
===================================================================
--- /branches/pap/Ohana/src/relphot/src/select_images.c	(revision 28483)
+++ /branches/pap/Ohana/src/relphot/src/select_images.c	(revision 28484)
@@ -101,5 +101,5 @@
     
     if (!FindMosaicForImage (timage, Ntimage, i)) {
-      fprintf (stderr, "cannot find mosaic for %lld\n", (long long) i);
+      fprintf (stderr, "cannot find mosaic for "OFF_T_FMT"\n", i);
       continue;
     }
@@ -175,5 +175,5 @@
   }
       
-  if (VERBOSE) fprintf (stderr, "found %lld images\n", (long long) nimage);
+  if (VERBOSE) fprintf (stderr, "found "OFF_T_FMT" images\n", nimage);
 
   REALLOCATE (image, Image, MAX (nimage, 1));
Index: /branches/pap/Ohana/src/relphot/src/setExclusions.c
===================================================================
--- /branches/pap/Ohana/src/relphot/src/setExclusions.c	(revision 28483)
+++ /branches/pap/Ohana/src/relphot/src/setExclusions.c	(revision 28484)
@@ -50,7 +50,7 @@
     }
   }
-  if (VERBOSE) fprintf (stderr, "%lld measurements marked by area\n",    (long long) Narea);
-  if (VERBOSE) fprintf (stderr, "%lld measurements marked nocal\n",      (long long) Nnocal);
-  if (VERBOSE) fprintf (stderr, "%lld measurements kept for analysis\n", (long long) Ngood);
+  if (VERBOSE) fprintf (stderr, OFF_T_FMT" measurements marked by area\n",    Narea);
+  if (VERBOSE) fprintf (stderr, OFF_T_FMT" measurements marked nocal\n",      Nnocal);
+  if (VERBOSE) fprintf (stderr, OFF_T_FMT" measurements kept for analysis\n", Ngood);
   return (TRUE);
 }
Index: /branches/pap/Ohana/src/relphot/src/setMrelFinal.c
===================================================================
--- /branches/pap/Ohana/src/relphot/src/setMrelFinal.c	(revision 28483)
+++ /branches/pap/Ohana/src/relphot/src/setMrelFinal.c	(revision 28484)
@@ -138,6 +138,6 @@
     }
   }
-  if (VERBOSE) fprintf (stderr, "pass %d, Ntot: %lld, Ntry: %lld, Nskip: %lld, Nkeep: %lld\n",
-			pass, (long long) Ntot, (long long) Ntry, (long long) Nskip, (long long) Nkeep);
+  if (VERBOSE) fprintf (stderr, "pass %d, Ntot: "OFF_T_FMT", Ntry: "OFF_T_FMT", Nskip: "OFF_T_FMT", Nkeep: "OFF_T_FMT"\n",
+			pass, Ntot, Ntry, Nskip, Nkeep);
 }
 
Index: /branches/pap/Ohana/src/tools/src/ftable.c
===================================================================
--- /branches/pap/Ohana/src/tools/src/ftable.c	(revision 28483)
+++ /branches/pap/Ohana/src/tools/src/ftable.c	(revision 28484)
@@ -1,4 +1,5 @@
 # include <ohana.h>
 # include <gfitsio.h>
+# include "inttypes.h"
 
 char *print_table_row (char *row, Header *header);
@@ -95,6 +96,6 @@
   table.datasize = Nbytes;
 
-  gfits_scan (table.header, "NAXIS1",  "%lld", 1, (long long *) &Nx);
-  gfits_scan (table.header, "NAXIS2",  "%lld", 1, (long long *) &Ny);
+  gfits_scan (table.header, "NAXIS1",  OFF_T_FMT, 1,  &Nx);
+  gfits_scan (table.header, "NAXIS2",  OFF_T_FMT, 1,  &Ny);
 
   /* print a column */
@@ -125,5 +126,5 @@
   char field[16], type[16], format[16], *line;
 
-  gfits_scan (header, "NAXIS1",  "%lld", 1, (long long *) &Nx);
+  gfits_scan (header, "NAXIS1",  OFF_T_FMT, 1,  &Nx);
   gfits_scan (header, "TFIELDS", "%d", 1, &Nfields);
 
@@ -209,9 +210,9 @@
     for (i = 0; i < Naxis; i++) {
       sprintf (axisname, "NAXIS%d", i+1);
-      status = gfits_scan (&header, axisname,  "%lld", 1, (long long *) &Nelem);
+      status = gfits_scan (&header, axisname,  OFF_T_FMT, 1,  &Nelem);
       if (!status) {
 	fprintf (stderr, "missing %s\n", axisname);
       }
-      fprintf (stdout, " %7lld", (long long) Nelem);
+      fprintf (stdout, " "OFF_T_FMT,  Nelem);
     }
     fprintf (stdout, "\n");
@@ -294,6 +295,6 @@
 
   gfits_scan (header, "TFIELDS", "%d", 1, &Nfields);
-  gfits_scan (header, "NAXIS1",  "%lld", 1, (long long *) &Nx);
-  gfits_scan (header, "NAXIS2",  "%lld", 1, (long long *) &Ny);
+  gfits_scan (header, "NAXIS1",  OFF_T_FMT, 1,  &Nx);
+  gfits_scan (header, "NAXIS2",  OFF_T_FMT, 1,  &Ny);
 
   if (Colname != (char *) NULL) {
@@ -358,5 +359,5 @@
 	  if (!strcmp (type, "int64_t")) {
 	    memcpy (line, &data[i*Nv*Nb + Nb*j], Nb);
-	    fprintf (stdout, "%lld ", (long long) *(int64_t*)line);
+	    fprintf (stdout, "%" PRId64" ",  *(int64_t*)line);
 	  }
 	  if (!strcmp (type, "float")) {
Index: /branches/pap/Ohana/src/uniphot/src/load_images.c
===================================================================
--- /branches/pap/Ohana/src/uniphot/src/load_images.c	(revision 28483)
+++ /branches/pap/Ohana/src/uniphot/src/load_images.c	(revision 28484)
@@ -23,5 +23,5 @@
   image = gfits_table_get_Image (&db[0].ftable, Nimage, &db[0].swapped);
 
-  fprintf (stderr, "loaded %lld images\n", (long long) *Nimage);
+  fprintf (stderr, "loaded "OFF_T_FMT" images\n", *Nimage);
 
   return (image);
Index: /branches/pap/Ohana/src/uniphot/src/update_catalog_setphot.c
===================================================================
--- /branches/pap/Ohana/src/uniphot/src/update_catalog_setphot.c	(revision 28483)
+++ /branches/pap/Ohana/src/uniphot/src/update_catalog_setphot.c	(revision 28484)
@@ -93,5 +93,5 @@
 
   if (found) {
-    fprintf (stderr, "found %lld matches\n", (long long) found);
+    fprintf (stderr, "found "OFF_T_FMT" matches\n", found);
   }
 }
Index: /branches/pap/PS-IPP-PStamp/lib/PS/IPP/PStamp/Job.pm
===================================================================
--- /branches/pap/PS-IPP-PStamp/lib/PS/IPP/PStamp/Job.pm	(revision 28483)
+++ /branches/pap/PS-IPP-PStamp/lib/PS/IPP/PStamp/Job.pm	(revision 28484)
@@ -475,5 +475,5 @@
     my $inverse = $option_mask & $PSTAMP_SELECT_INVERSE;
 
-    my $command = "$difftool -dbname $imagedb";
+    my $command = "$difftool -dbname $imagedb -pstamp_order";
     
     my $listrun = 0;
@@ -482,6 +482,9 @@
             $command .= " -diffskyfile -diff_id $id -skycell_id $skycell_id";
         } else {
+            $listrun = 1;
             $command .= " -listrun -diff_id $id";
-            $listrun = 1;
+            # the following is a work around for the problem reported in ticket #1394
+            # 'difftool -listrun returns multiple rows for stack-stack diffs'
+            $command .= " -limit 1";
         }
     } else {
@@ -492,5 +495,5 @@
 
     my $n = $output ? scalar @$output : 0;
-    if (!$listrun && ($n > 1)) {
+    if ($n > 1) {
         die ("difftool returned an unexpected number of diffskyfiles: $n");
     } elsif ($n == 0) {
Index: /branches/pap/dbconfig/add.md
===================================================================
--- /branches/pap/dbconfig/add.md	(revision 28483)
+++ /branches/pap/dbconfig/add.md	(revision 28484)
@@ -12,4 +12,7 @@
     note            STR     255
     image_only      BOOL    f
+    minidvodb	    BOOL    f
+    minidvodb_group STR	    64
+    minidvodb_name  STR     64
 END
 
@@ -18,4 +21,5 @@
     dtime_addstar   F32     0.0
     path_base	    STR	    255
+    dvodb_path      STR	    255
     fault           S16     0       # Key NOT NULL
 END
@@ -24,3 +28,5 @@
     label           STR    64       # Primary Key
 END
-    
+
+
+
Index: /branches/pap/dbconfig/changes.txt
===================================================================
--- /branches/pap/dbconfig/changes.txt	(revision 28483)
+++ /branches/pap/dbconfig/changes.txt	(revision 28484)
@@ -1634,4 +1634,5 @@
 ALTER TABLE diffSkyfile ADD COLUMN maskfrac_advisory FLOAT after maskfrac_magic;
 
+
 ALTER TABLE chipRun CHANGE maskfrac_npix  maskfrac_npix FLOAT;
 ALTER TABLE camRun CHANGE maskfrac_npix  maskfrac_npix FLOAT;
@@ -1811,2 +1812,126 @@
     FOREIGN KEY(warp_bg_id) REFERENCES warpBackgroundRun(warp_bg_id)
 ) ENGINE=innodb DEFAULT CHARSET=latin1;
+
+
+-- Version 1.1.68 addition of minidvodb tables and changes to addTables 
+
+-- this modifies addTables
+
+ALTER TABLE addRun ADD COLUMN minidvodb tinyint NOT NULL AFTER image_only;
+ALTER TABLE addRun ADD COLUMN minidvodb_group varchar(64) AFTER minidvodb;
+ALTER TABLE addRun ADD COLUMN minidvodb_name varchar(64) AFTER minidvodb_group;
+
+ALTER TABLE addProcessedExp ADD COLUMN dvodb_path varchar(255) AFTER path_base;
+
+
+-- this adds the new tables 
+
+CREATE TABLE minidvodbRun (
+minidvodb_id BIGINT AUTO_INCREMENT,
+                    minidvodb_name VARCHAR(64),
+              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,
+       PRIMARY KEY(minidvodb_id), KEY(minidvodb_name), INDEX(minidvodb_id)
+) ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE minidvodbProcessed (
+      minidvodb_id BIGINT,
+      merge_order BIGINT,
+dtime_resort FLOAT,
+dtime_relphot FLOAT,
+dtime_merge FLOAT,
+epoch TIMESTAMP,
+mergedvodb_path VARCHAR(255) NOT NULL,
+fault SMALLINT NOT NULL,
+       KEY(minidvodb_id), CONSTRAINT UNIQUE(minidvodb_id), FOREIGN KEY(minidvodb_id) REFERENCES minidvodbRun (minidvodb_id)
+) ENGINE=innodb DEFAULT CHARSET=latin1;
+
+ALTER TABLE pstampRequest ADD COLUMN timestamp TIMESTAMP AFTER outdir;
+
+
+-- Tables to support (re-)photometry of a diff
+CREATE TABLE diffPhotRun (
+    diff_phot_id BIGINT AUTO_INCREMENT, -- Identifier for diffPhotRun
+    diff_id BIGINT NOT NULL,            -- Identifier for diffRun
+    state VARCHAR(64) NOT NULL,         -- State of run
+    workdir VARCHAR(255) NOT NULL, -- working directory
+    label VARCHAR(64),             -- processing label
+    data_group VARCHAR(64),        -- group for data
+    reduction VARCHAR(64),         -- reduction class (for altering recipe)
+    registered TIMESTAMP DEFAULT CURRENT_TIMESTAMP, -- time run was registered
+    note VARCHAR(255),             -- note
+    PRIMARY KEY(diff_phot_id),
+    KEY(diff_id),
+    KEY(state),
+    KEY(label),
+    KEY(data_group),
+    FOREIGN KEY(diff_id) REFERENCES diffRun(diff_id)
+) ENGINE=innodb DEFAULT CHARSET=latin1;
+CREATE TABLE diffPhotSkyfile (
+    diff_phot_id BIGINT AUTO_INCREMENT, -- Identifier for diffPhotRun
+    skycell_id VARCHAR(64) NOT NULL,            -- Skycell identifier
+    path_base VARCHAR(255) NOT NULL, -- Base of path for output
+    dtime_script FLOAT,              -- elapsed time for script
+    hostname VARCHAR(64) NOT NULL,   -- host that executed script
+    fault SMALLINT NOT NULL,         -- fault code
+    quality SMALLINT NOT NULL,       -- bad quality flag
+    software_ver VARCHAR(16),                       -- software version for run
+    magicked BIGINT NOT NULL DEFAULT 0, -- magic mask applied
+    PRIMARY KEY(diff_phot_id, skycell_id),
+    KEY(fault),
+    KEY(quality),
+    FOREIGN KEY(diff_phot_id) REFERENCES diffPhotRun(diff_phot_id)
+) ENGINE=innodb DEFAULT CHARSET=latin1;
+
+
+-- Version 1.1.69 warp/stack/diff summary and stack associations.
+
+CREATE TABLE warpSummary (
+    warp_id     BIGINT,
+    projection_cell VARCHAR(64) NOT NULL,
+    path_base   VARCHAR(255) NOT NULL,
+    PRIMARY KEY(warp_id),
+    KEY(projection_cell),
+    FOREIGN KEY(warp_id) REFERENCES warpRun(warp_id)
+) ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE diffSummary (
+    diff_id     BIGINT,
+    projection_cell VARCHAR(64) NOT NULL,
+    path_base   VARCHAR(255) NOT NULL,
+    PRIMARY KEY(diff_id),
+    KEY(projection_cell),
+    FOREIGN KEY(diff_id) REFERENCES diffRun(diff_id)
+) ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE stackAssociation (
+    sass_id      BIGINT AUTO_INCREMENT,
+    data_group   VARCHAR(64) NOT NULL,
+    projection_cell VARCHAR(64) NOT NULL,
+    tess_id       VARCHAR(64) NOT NULL,
+    filter        VARCHAR(64) NOT NULL,
+    PRIMARY KEY(sass_id),
+    KEY(data_group),
+    KEY(projection_cell),
+    KEY(tess_id)
+) ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE stackAssociationMap (
+    sass_id      BIGINT,
+    stack_id     BIGINT,
+    PRIMARY KEY(sass_id, stack_id),
+    FOREIGN KEY(sass_id) REFERENCES stackAssociation(sass_id),
+    FOREIGN KEY(stack_id) REFERENCES stackRun(stack_id)
+) ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE stackSummary (
+    sass_id     BIGINT,
+    projection_cell VARCHAR(64) NOT NULL,
+    path_base   VARCHAR(255) NOT NULL,
+    PRIMARY KEY(sass_id),
+    KEY(projection_cell),
+    FOREIGN KEY(sass_id) REFERENCES stackAssociation(sass_id)
+) ENGINE=innodb DEFAULT CHARSET=latin1;
Index: /branches/pap/dbconfig/diff.md
===================================================================
--- /branches/pap/dbconfig/diff.md	(revision 28483)
+++ /branches/pap/dbconfig/diff.md	(revision 28484)
@@ -75,2 +75,8 @@
     maskfrac_advisory F32   0.0
 END
+
+diffSummary METADATA
+    diff_id	    S64      0       # Primary Key fkey(diff_id) ref diffRun(diff_id)
+    projection_cell STR	    32	    # Primary Key
+    path_base 	    STR	    255
+END
Index: /branches/pap/dbconfig/diffphot.md
===================================================================
--- /branches/pap/dbconfig/diffphot.md	(revision 28484)
+++ /branches/pap/dbconfig/diffphot.md	(revision 28484)
@@ -0,0 +1,24 @@
+			
+diffPhotRun METADATA
+    diff_phot_id    S64         0       # Primary Key AUTO_INCREMENT
+    diff_id	    S64		0	# Key
+    state           STR         64      # Key
+    workdir         STR         255
+    label           STR         64      # Key
+    data_group      STR         64      # Key
+    reduction       STR         64
+    registered      TAI         NULL
+    note            STR         255
+END
+
+diffPhotSkyfile METADATA
+    diff_phot_id     S64        0       # Primary Key
+    skycell_id       STR        64      # Primary Key
+    path_base        STR        255
+    dtime_script     F32        0.0
+    hostname         STR        64
+    fault            S16        0       # Key
+    quality          S16        0       # Key
+    software_ver     STR        16
+    magicked         S64        0
+END
Index: /branches/pap/dbconfig/ipp.m4
===================================================================
--- /branches/pap/dbconfig/ipp.m4	(revision 28483)
+++ /branches/pap/dbconfig/ipp.m4	(revision 28484)
@@ -24,4 +24,5 @@
 include(label.md)
 include(magic.md)
+include(minidvodb.md)
 include(calibration.md)
 include(flatcorr.md)
@@ -33,2 +34,3 @@
 include(publish.md)
 include(background.md)
+include(diffphot.md)
Index: /branches/pap/dbconfig/minidvodb.md
===================================================================
--- /branches/pap/dbconfig/minidvodb.md	(revision 28484)
+++ /branches/pap/dbconfig/minidvodb.md	(revision 28484)
@@ -0,0 +1,20 @@
+minidvodbRun METADATA
+    minidvodb_id    S64    0	     
+    minidvodb_name  STR    64
+    minidvodb_group STR    64
+    minidvodb_path  STR    255
+    mergedvodb_path STR    255
+    state           STR    64
+    creation_date   UTC    0001-01-01T00:00:00Z
+END 
+
+minidvodbProcessed METADATA
+    minidvodb_id    S64    0
+    merge_order     S64    0
+    dtime_resort    F32    0.0
+    dtime_relphot   F32    0.0
+    dtime_merge     F32    0.0
+    epoch           UTC    0001-01-01T00:00:00Z
+    mergedvodb_path STR    255
+    fault	    S16	   0
+END
Index: /branches/pap/dbconfig/pstamp.md
===================================================================
--- /branches/pap/dbconfig/pstamp.md	(revision 28483)
+++ /branches/pap/dbconfig/pstamp.md	(revision 28484)
@@ -31,4 +31,5 @@
     uri         STR         255
     outdir      STR         255
+    timestamp   UTC         0001-01-01T00:00:00Z
     fault       S32         0
 END
Index: /branches/pap/dbconfig/stack.md
===================================================================
--- /branches/pap/dbconfig/stack.md	(revision 28483)
+++ /branches/pap/dbconfig/stack.md	(revision 28484)
@@ -53,2 +53,21 @@
     quality            S16    0
 END
+
+stackSummary METADATA
+    sass_id	   S64      0       # Primary Key fkey(sass_id) ref stackAssociation(sass_id)
+    projection_cell  STR	    32	    # Primary Key
+    path_base 	   STR	    255
+END
+
+stackAssociation METADATA
+    sass_id        S64      0       # Primary Key AUTO_INCREMENT
+    data_group     STR      64      # Key
+    projection_cell  STR      64      # Key
+    tess_id        STR      64      # Key
+    filter         STR      64
+END
+
+stackAssociationMap METADATA
+    sass_id        S64      0       # Primary Key fkey(sass_id) ref stackAssociation(sass_id)
+    stack_id       S64      0       # Primary Key fkey(stack_id) ref stackRun(stack_id)
+END
Index: /branches/pap/dbconfig/warp.md
===================================================================
--- /branches/pap/dbconfig/warp.md	(revision 28483)
+++ /branches/pap/dbconfig/warp.md	(revision 28484)
@@ -79,2 +79,8 @@
     label       STR         64      # Primary Key
 END
+
+warpSummary METADATA
+    warp_id	   S64      0       # Primary Key fkey(warp_id) ref warpRun(warp_id)
+    projection_cell  STR	    32	    # Primary Key
+    path_base 	   STR	    255
+END
Index: /branches/pap/ippMonitor/Makefile.in
===================================================================
--- /branches/pap/ippMonitor/Makefile.in	(revision 28483)
+++ /branches/pap/ippMonitor/Makefile.in	(revision 28484)
@@ -16,5 +16,7 @@
 $(DESTBIN)/czartool_getLabels.pl \
 $(DESTBIN)/czartool_checkServer.pl \
-$(DESTBIN)/czartool_getServerStatus.pl
+$(DESTBIN)/czartool_getServerStatus.pl \
+$(DESTBIN)/build_histogram.dvo \
+
 
 RAWSRC = \
@@ -50,4 +52,6 @@
 $(DESTWWW)/czartool_labels.php \
 $(DESTWWW)/czartool_servers.php \
+$(DESTWWW)/histogram.php \
+$(DESTWWW)/show_and_delete_image.php \
 
 
@@ -121,4 +125,5 @@
 $(DESTWWW)/rawScienceExp.php \
 $(DESTWWW)/rawExp.php \
+$(DESTWWW)/rawSummary.php \
 $(DESTWWW)/rawExpStats.php \
 $(DESTWWW)/summitExp.php \
@@ -171,5 +176,6 @@
 $(DESTWWW)/flatcorrRun.php \
 $(DESTWWW)/flatcorrChip.php \
-$(DESTWWW)/flatcorrCamera.php
+$(DESTWWW)/flatcorrCamera.php \
+$(DESTWWW)/maskStats.php
 
 PICTURES = \
Index: /branches/pap/ippMonitor/def/maskStats.d
===================================================================
--- /branches/pap/ippMonitor/def/maskStats.d	(revision 28484)
+++ /branches/pap/ippMonitor/def/maskStats.d	(revision 28484)
@@ -0,0 +1,50 @@
+TABLE rawExp, chipRun, camRun, fakeRun, warpRun
+TITLE Mask Stats
+FILE  maskStats.php
+MENU  ipp.imfiles.dat
+
+WHERE rawExp.exp_id = chipRun.exp_id
+WHERE chipRun.chip_id = camRun.chip_id
+WHERE camRun.cam_id = fakeRun.cam_id
+WHERE fakeRun.fake_id = warpRun.fake_id
+
+OP   OP1  $ra * 57.295783
+OP   OP2  $decl * 57.295783
+
+#        field              size format  name           show          link to   extras
+FIELD    rawExp.exp_name,    10,  %s,     Exp Name
+FIELD    rawExp.exp_id,       5,  %d,     Exp ID,        value,   rawImfile.php,  ARG1
+FIELD    rawExp.exp_type,     8,  %s,     Type    
+FIELD    rawExp.state,        5,  %s,     State
+FIELD    rawExp.obs_mode,     8,  %s,     obs mode
+FIELD    rawExp.dateobs,     19,  %T,     Date/Time
+FIELD    rawExp.ra,           8,  %C,     RA,           op=OP1      
+FIELD    rawExp.decl,         8,  %C,     DEC,          op=OP2
+FIELD    rawExp.filter,      10,  %s,     FILTER
+FIELD    rawExp.airmass,      5,  %.4f,   airmass     
+FIELD    rawExp.exp_time,     5,  %.2f,   exp_time    
+
+FIELD    chipRun.maskfrac_npix,         8,  %.3e,     chip npix   
+FIELD    camRun.maskfrac_ref_npix,     	8,  %.3e,     cam ref npix   
+FIELD    camRun.maskfrac_max_npix,     	8,  %.3e,     cam max npix   
+FIELD    warpRun.maskfrac_npix,     	8,  %.3e,     warp npix   
+
+FIELD    chipRun.maskfrac_static,   	8,  %.3f,     chip static 
+FIELD    camRun.maskfrac_ref_static,   	8,  %.3f,     cam ref static 
+FIELD    camRun.maskfrac_max_static,   	8,  %.3f,     cam max static 
+FIELD    warpRun.maskfrac_static,   	8,  %.3f,     warp static 
+
+FIELD    chipRun.maskfrac_dynamic,  	8,  %.3f,     chip dynamic
+FIELD    camRun.maskfrac_ref_dynamic,  	8,  %.3f,     cam ref dynamic
+FIELD    camRun.maskfrac_max_dynamic,  	8,  %.3f,     cam max dynamic
+FIELD    warpRun.maskfrac_dynamic,  	8,  %.3f,     warp dynamic
+
+FIELD    chipRun.maskfrac_magic,    	8,  %.3f,     chip magic  
+FIELD    camRun.maskfrac_ref_magic,    	8,  %.3f,     cam ref magic  
+FIELD    camRun.maskfrac_max_magic,    	8,  %.3f,     cam max magic  
+FIELD    warpRun.maskfrac_magic,    	8,  %.3f,     warp magic  
+
+FIELD    chipRun.maskfrac_advisory, 	8,  %.3f,     chip advisory
+FIELD    camRun.maskfrac_ref_advisory, 	8,  %.3f,     cam ref advisory
+FIELD    camRun.maskfrac_max_advisory, 	8,  %.3f,     cam max advisory
+FIELD    warpRun.maskfrac_advisory,     8,  %.3f,     warp advisory 
Index: /branches/pap/ippMonitor/def/rawExp.d
===================================================================
--- /branches/pap/ippMonitor/def/rawExp.d	(revision 28483)
+++ /branches/pap/ippMonitor/def/rawExp.d	(revision 28484)
@@ -15,4 +15,5 @@
 FIELD    camera,      10,  %s,     Camera
 FIELD    exp_type,     8,  %s,     Type    
+FIELD    obs_mode,     8,  %s,     obs mode
 FIELD    dateobs,     19,  %T,     Date/Time
 FIELD    ra,           8,  %C, RA,           op=OP1      
Index: /branches/pap/ippMonitor/def/rawSummary.d
===================================================================
--- /branches/pap/ippMonitor/def/rawSummary.d	(revision 28484)
+++ /branches/pap/ippMonitor/def/rawSummary.d	(revision 28484)
@@ -0,0 +1,17 @@
+TABLE rawExp
+TITLE Raw Summary
+FILE  rawSummary.php
+MENU  ipp.load.dat
+
+#     field            size  format  name         show    link to         extras
+FIELD exp_type,    	 15, %s,     OBSTYPE
+FIELD obs_mode,    	 15, %s,     OBS_MODE
+FIELD state,    	  7, %s,     state
+FIELD MIN(dateobs),    	  19, %T,    min date
+FIELD MAX(dateobs),    	  19, %T,    max date
+FIELD count(exp_id) as n, 7, %d,     count
+
+MODE  summary
+GROUP state
+GROUP exp_type
+GROUP obs_mode
Index: /branches/pap/ippMonitor/raw/czartool_labels.php
===================================================================
--- /branches/pap/ippMonitor/raw/czartool_labels.php	(revision 28483)
+++ /branches/pap/ippMonitor/raw/czartool_labels.php	(revision 28484)
@@ -95,9 +95,9 @@
         $str = "";
         $anyFaults = false; 
-        $link = "chipProcessedImfile_failure.php?menu=ipp.science.dat&pass=" . $pass . "&proj=" . $proj . "&label=" . $stdsLabel;
+        $link = "chipProcessedImfile_failure.php?pass=" . $pass . "&proj=" . $proj . "&chipRun.label=" . $label . "&chipRun.state=new";
         getStateAndFaults($db, $label,"chipRun", $state, "chip", $str, $anyFaults);
         write_table_cell($class, '%s', $anyFaults ? $link : "", $str);
 
-        $link = "camProcessedExp_failure.php?pass=" . $pass . "&proj=" . $proj . "&label=" . $stdsLabel;
+        $link = "camProcessedExp_failure.php?pass=" . $pass . "&proj=" . $proj . "&camRun.label=" . $label . "&camRun.state=new";
         getStateAndFaults($db, $label,"camRun", $state, "cam", $str, $anyFaults);
         write_table_cell($class, '%s',  $anyFaults ? $link : "", $str);
@@ -107,13 +107,13 @@
         write_table_cell($class, '%s',  $anyFaults ? $link : "", $str);
 
-        $link = "warpFailedSkyfiles.php?pass=" . $pass . "&proj=" . $proj;
+        $link = "warpFailedSkyfiles.php?pass=" . $pass . "&proj=" . $proj . "&warpRun.label=" . $label . "&warpRun.state=new";
         getStateAndFaults($db, $label,"warpRun", $state, "warp", $str, $anyFaults);
         write_table_cell($class, '%s',  $anyFaults ? $link : "",  $str);
         
-        $link = "stackFailedSkyfile.php?pass=" . $pass . "&proj=" . $proj;
+        $link = "stackFailedSkyfile.php?pass=" . $pass . "&proj=" . $proj . "&stackRun.label=" . $label . "&stackRun.state=new";
         getStateAndFaults($db, $label,"stackRun", $state, "stack", $str, $anyFaults);
         write_table_cell($class, '%s',  $anyFaults ? $link : "", $str);
 
-        $link = "diffFailedSkyfile.php?pass=" . $pass . "&proj=" . $proj;
+        $link = "diffFailedSkyfile.php?pass=" . $pass . "&proj=" . $proj . "&diffRun.label=" . $label . "&diffRun.state=new";
         getStateAndFaults($db, $label,"diffRun", $state, "diff", $str, $anyFaults);
         write_table_cell($class, '%s',  $anyFaults ? $link : "",  $str);
@@ -189,9 +189,9 @@
         $anyFaults = false; 
 
-        $link = "chipProcessedImfile_failure.php?menu=ipp.science.dat&pass=" . $pass . "&proj=" . $proj . "&label=" . $stdsLabel;
+        $link = "chipProcessedImfile_failure.php?pass=" . $pass . "&proj=" . $proj . "&chipRun.label=" . $stdsLabel . "&chipRun.state=new";
         getStateAndFaults($db, $stdsLabel,"chipRun", $selectedState, "chip", $str, $anyFaults);
         write_table_cell($class, '%s', $anyFaults ? $link : "", $str);
 
-        $link = "camProcessedExp_failure.php?pass=" . $pass . "&proj=" . $proj . "&label=" . $stdsLabel;
+        $link = "camProcessedExp_failure.php?pass=" . $pass . "&proj=" . $proj . "&camRun.label=" . $stdsLabel . "&camRun.state=new";
         getStateAndFaults($db, $stdsLabel,"camRun", $selectedState, "cam", $str, $anyFaults);
         write_table_cell($class, '%s',  $anyFaults ? $link : "", $str);
@@ -201,13 +201,13 @@
         write_table_cell($class, '%s',  $anyFaults ? $link : "", $str);
 
-        $link = "warpFailedSkyfiles.php?pass=" . $pass . "&proj=" . $proj;
+        $link = "warpFailedSkyfiles.php?pass=" . $pass . "&proj=" . $proj . "&warpRun.label=" . $stdsLabel . "&warpRun.state=new";
         getStateAndFaults($db, $stdsLabel,"warpRun", $selectedState, "warp", $str, $anyFaults);
         write_table_cell($class, '%s',  $anyFaults ? $link : "",  $str);
         
-        $link = "stackFailedSkyfile.php?pass=" . $pass . "&proj=" . $proj;
+        $link = "stackFailedSkyfile.php?pass=" . $pass . "&proj=" . $proj . "&stackRun.label=" . $stdsLabel . "&stackRun.state=new";
         getStateAndFaults($db, $stdsLabel,"stackRun", $selectedState, "stack", $str, $anyFaults);
         write_table_cell($class, '%s',  $anyFaults ? $link : "", $str);
 
-        $link = "diffFailedSkyfile.php?pass=" . $pass . "&proj=" . $proj;
+        $link = "diffFailedSkyfile.php?pass=" . $pass . "&proj=" . $proj . "&diffRun.label=" . $stdsLabel . "&diffRun.state=new";
         getStateAndFaults($db, $stdsLabel,"diffRun", $selectedState, "diff", $str, $anyFaults);
         write_table_cell($class, '%s',  $anyFaults ? $link : "",  $str);
Index: /branches/pap/ippMonitor/raw/histogram.php
===================================================================
--- /branches/pap/ippMonitor/raw/histogram.php	(revision 28484)
+++ /branches/pap/ippMonitor/raw/histogram.php	(revision 28484)
@@ -0,0 +1,472 @@
+<?php 
+
+$debug = 0;
+
+include 'ipp.php';
+
+$ID = checkID ();
+
+$filesToRemove = array();
+
+// require an explicit project
+if (! $ID['proj']) { projectform ($ID); }
+
+$db = dbconnect($ID['proj']);
+
+if ($ID['menu']) {
+  $myMenu = $ID['menu'];
+} else {
+  $myMenu = "ipp.imfiles.dat";
+}
+
+menu($myMenu, 'Histogram', 'ipp.css', $ID['link'], $ID['proj']);
+
+echo "<p> Histogram </p>";
+
+// set up the form
+echo "<form action=\"histogram.php\" method=\"POST\">\n";
+
+$restricted = 0;
+
+// define restrictiosn to the queries
+// ** TABLE RESTRICTIONS **
+$WHERE = "WHERE camRun.state = 'full' AND camRun.chip_id = chipRun.chip_id AND chipRun.exp_id = rawExp.exp_id AND camProcessedExp.cam_id  = camRun.cam_id AND camProcessedExp.fault = 0";
+$WHERE = check_restrict ('rawExp.exp_name', $WHERE, 'string', 1.0);
+$WHERE = check_restrict ('chipRun.label', $WHERE, 'string', 1.0);
+$WHERE = check_restrict ('chipRun.data_group', $WHERE, 'string', 1.0);
+$WHERE = check_restrict ('chipRun.dist_group', $WHERE, 'string', 1.0);
+$WHERE = check_restrict ('rawExp.dateobs', $WHERE, 'string', 1.0);
+$WHERE = check_restrict ('rawExp.dateobs', $WHERE, 'min', 1.0);
+$WHERE = check_restrict ('rawExp.dateobs', $WHERE, 'max', 1.0);
+$WHERE = check_restrict ('rawExp.filter', $WHERE, 'string', 1.0);
+$WHERE = check_restrict ('rawExp.exp_time', $WHERE, 'string', 1.0);
+$WHERE = check_restrict ('rawExp.exp_time', $WHERE, 'min', 1.0);
+$WHERE = check_restrict ('rawExp.exp_time', $WHERE, 'max', 1.0);
+$WHERE = check_restrict ('rawExp.airmass', $WHERE, 'string', 1.0);
+$WHERE = check_restrict ('rawExp.airmass', $WHERE, 'min', 1.0);
+$WHERE = check_restrict ('rawExp.airmass', $WHERE, 'max', 1.0);
+$WHERE = check_restrict ('camProcessedExp.fwhm_major', $WHERE, 'string', 1.0);
+$WHERE = check_restrict ('camProcessedExp.fwhm_major', $WHERE, 'min', 1.0);
+$WHERE = check_restrict ('camProcessedExp.fwhm_major', $WHERE, 'max', 1.0);
+$WHERE = check_restrict ('camProcessedExp.fwhm_minor', $WHERE, 'string', 1.0);
+$WHERE = check_restrict ('camProcessedExp.fwhm_minor', $WHERE, 'min', 1.0);
+$WHERE = check_restrict ('camProcessedExp.fwhm_minor', $WHERE, 'max', 1.0);
+$WHERE = check_restrict ('camProcessedExp.fwhm_major_uq', $WHERE, 'string', 1.0);
+$WHERE = check_restrict ('camProcessedExp.fwhm_major_uq', $WHERE, 'min', 1.0);
+$WHERE = check_restrict ('camProcessedExp.fwhm_major_uq', $WHERE, 'max', 1.0);
+$WHERE = check_restrict ('camProcessedExp.fwhm_major_lq', $WHERE, 'string', 1.0);
+$WHERE = check_restrict ('camProcessedExp.fwhm_major_lq', $WHERE, 'min', 1.0);
+$WHERE = check_restrict ('camProcessedExp.fwhm_major_lq', $WHERE, 'max', 1.0);
+$WHERE = check_restrict ('camProcessedExp.fwhm_minor_uq', $WHERE, 'string', 1.0);
+$WHERE = check_restrict ('camProcessedExp.fwhm_minor_uq', $WHERE, 'min', 1.0);
+$WHERE = check_restrict ('camProcessedExp.fwhm_minor_uq', $WHERE, 'max', 1.0);
+$WHERE = check_restrict ('camProcessedExp.fwhm_minor_lq', $WHERE, 'string', 1.0);
+$WHERE = check_restrict ('camProcessedExp.fwhm_minor_lq', $WHERE, 'min', 1.0);
+$WHERE = check_restrict ('camProcessedExp.fwhm_minor_lq', $WHERE, 'max', 1.0);
+$WHERE = check_restrict ('camProcessedExp.iq_fwhm_major', $WHERE, 'string', 1.0);
+$WHERE = check_restrict ('camProcessedExp.iq_fwhm_major', $WHERE, 'min', 1.0);
+$WHERE = check_restrict ('camProcessedExp.iq_fwhm_major', $WHERE, 'max', 1.0);
+$WHERE = check_restrict ('camProcessedExp.iq_fwhm_minor', $WHERE, 'string', 1.0);
+$WHERE = check_restrict ('camProcessedExp.iq_fwhm_minor', $WHERE, 'min', 1.0);
+$WHERE = check_restrict ('camProcessedExp.iq_fwhm_minor', $WHERE, 'max', 1.0);
+$WHERE = check_restrict ('camProcessedExp.iq_m2', $WHERE, 'string', 1.0);
+$WHERE = check_restrict ('camProcessedExp.iq_m2', $WHERE, 'min', 1.0);
+$WHERE = check_restrict ('camProcessedExp.iq_m2', $WHERE, 'max', 1.0);
+$WHERE = check_restrict ('camProcessedExp.iq_m3', $WHERE, 'string', 1.0);
+$WHERE = check_restrict ('camProcessedExp.iq_m3', $WHERE, 'min', 1.0);
+$WHERE = check_restrict ('camProcessedExp.iq_m3', $WHERE, 'max', 1.0);
+$WHERE = check_restrict ('camProcessedExp.iq_m4', $WHERE, 'string', 1.0);
+$WHERE = check_restrict ('camProcessedExp.iq_m4', $WHERE, 'min', 1.0);
+$WHERE = check_restrict ('camProcessedExp.iq_m4', $WHERE, 'max', 1.0);
+$WHERE = check_ordering ('', $WHERE);
+
+if ($restricted == 0) {
+  if ("none" != "none") {
+    $WHERE = "$WHERE none";
+  }
+}
+
+// get the result table count
+if (basic == "basic") {
+  $sql = "SELECT count(*) FROM camRun, chipRun, rawExp, camProcessedExp $WHERE";
+}
+if (basic == "summary") {
+  $sql = "SELECT count(*) FROM (SELECT rawExp.exp_name,chipRun.label,chipRun.data_group,chipRun.dist_group,rawExp.dateobs,rawExp.filter,rawExp.exp_time,rawExp.airmass,camProcessedExp.fwhm_major,camProcessedExp.fwhm_minor,camProcessedExp.fwhm_major_uq,camProcessedExp.fwhm_major_lq,camProcessedExp.fwhm_minor_uq,camProcessedExp.fwhm_minor_lq,camProcessedExp.iq_fwhm_major,camProcessedExp.iq_fwhm_minor,camProcessedExp.iq_m2,camProcessedExp.iq_m3,camProcessedExp.iq_m4 FROM camRun, chipRun, rawExp, camProcessedExp $WHERE) as TEMP";
+}
+
+$qry = $db->query($sql);
+if (dberror($qry)) {
+  echo "<b>error reading camRun, chipRun, rawExp, camProcessedExp table count</b><br>\n";
+  echo "<br><small><b> count query : $sql </b></small><br>\n";
+  menu_end();
+}
+if (!$qry->fetchInto($row)) {
+  echo "<b>error reading camRun, chipRun, rawExp, camProcessedExp table count</b><br>\n";
+  echo "<br><small><b> count query : $sql </b></small><br>\n";
+  menu_end();
+}
+// set up the row counter variables
+if ($ID['from']) {
+  $rowStart = $ID['from'];
+} else {
+  $rowStart = 0;
+}
+$rowLast = $rowStart + $dTABLE;
+$rowTotal = $row[0];
+if ($rowLast > $rowTotal) { $rowLast = $rowTotal; }
+echo "<b> $rowStart to $rowLast of $rowTotal items</b><br>\n";
+
+// query the database
+if (basic == "basic") {
+  $sql = "SELECT rawExp.exp_name,chipRun.label,chipRun.data_group,chipRun.dist_group,rawExp.dateobs,rawExp.filter,rawExp.exp_time,rawExp.airmass,camProcessedExp.fwhm_major,camProcessedExp.fwhm_minor,camProcessedExp.fwhm_major_uq,camProcessedExp.fwhm_major_lq,camProcessedExp.fwhm_minor_uq,camProcessedExp.fwhm_minor_lq,camProcessedExp.iq_fwhm_major,camProcessedExp.iq_fwhm_minor,camProcessedExp.iq_m2,camProcessedExp.iq_m3,camProcessedExp.iq_m4 FROM camRun, chipRun, rawExp, camProcessedExp $WHERE";
+}
+if (basic == "summary") {
+  $sql = "SELECT rawExp.exp_name,chipRun.label,chipRun.data_group,chipRun.dist_group,rawExp.dateobs,rawExp.filter,rawExp.exp_time,rawExp.airmass,camProcessedExp.fwhm_major,camProcessedExp.fwhm_minor,camProcessedExp.fwhm_major_uq,camProcessedExp.fwhm_major_lq,camProcessedExp.fwhm_minor_uq,camProcessedExp.fwhm_minor_lq,camProcessedExp.iq_fwhm_major,camProcessedExp.iq_fwhm_minor,camProcessedExp.iq_m2,camProcessedExp.iq_m3,camProcessedExp.iq_m4 FROM camRun, chipRun, rawExp, camProcessedExp $WHERE";
+}
+
+$qry = $db->query($sql);
+if (dberror($qry)) {
+  echo "<b>error reading camRun, chipRun, rawExp, camProcessedExp table</b><br>\n";
+  echo "<br><small><b> table query : $sql </b></small><br>\n";
+  menu_end();
+}
+
+// ** HEAD CODE **
+
+// ** BUTTON RESTRICTIONS **
+$buttonLink = button_restrict_string ('rawExp.exp_name', $buttonLink);
+$buttonLink = button_restrict_string ('chipRun.label', $buttonLink);
+$buttonLink = button_restrict_string ('chipRun.data_group', $buttonLink);
+$buttonLink = button_restrict_string ('chipRun.dist_group', $buttonLink);
+$buttonLink = button_restrict_string ('rawExp.dateobs', $buttonLink);
+$buttonLink = button_restrict_min ('rawExp.dateobs', $buttonLink);
+$buttonLink = button_restrict_max ('rawExp.dateobs', $buttonLink);
+$buttonLink = button_restrict_string ('rawExp.filter', $buttonLink);
+$buttonLink = button_restrict_string ('rawExp.exp_time', $buttonLink);
+$buttonLink = button_restrict_min ('rawExp.exp_time', $buttonLink);
+$buttonLink = button_restrict_max ('rawExp.exp_time', $buttonLink);
+$buttonLink = button_restrict_string ('rawExp.airmass', $buttonLink);
+$buttonLink = button_restrict_min ('rawExp.airmass', $buttonLink);
+$buttonLink = button_restrict_max ('rawExp.airmass', $buttonLink);
+$buttonLink = button_restrict_string ('camProcessedExp.fwhm_major', $buttonLink);
+$buttonLink = button_restrict_min ('camProcessedExp.fwhm_major', $buttonLink);
+$buttonLink = button_restrict_max ('camProcessedExp.fwhm_major', $buttonLink);
+$buttonLink = button_restrict_string ('camProcessedExp.fwhm_minor', $buttonLink);
+$buttonLink = button_restrict_min ('camProcessedExp.fwhm_minor', $buttonLink);
+$buttonLink = button_restrict_max ('camProcessedExp.fwhm_minor', $buttonLink);
+$buttonLink = button_restrict_string ('camProcessedExp.fwhm_major_uq', $buttonLink);
+$buttonLink = button_restrict_min ('camProcessedExp.fwhm_major_uq', $buttonLink);
+$buttonLink = button_restrict_max ('camProcessedExp.fwhm_major_uq', $buttonLink);
+$buttonLink = button_restrict_string ('camProcessedExp.fwhm_major_lq', $buttonLink);
+$buttonLink = button_restrict_min ('camProcessedExp.fwhm_major_lq', $buttonLink);
+$buttonLink = button_restrict_max ('camProcessedExp.fwhm_major_lq', $buttonLink);
+$buttonLink = button_restrict_string ('camProcessedExp.fwhm_minor_uq', $buttonLink);
+$buttonLink = button_restrict_min ('camProcessedExp.fwhm_minor_uq', $buttonLink);
+$buttonLink = button_restrict_max ('camProcessedExp.fwhm_minor_uq', $buttonLink);
+$buttonLink = button_restrict_string ('camProcessedExp.fwhm_minor_lq', $buttonLink);
+$buttonLink = button_restrict_min ('camProcessedExp.fwhm_minor_lq', $buttonLink);
+$buttonLink = button_restrict_max ('camProcessedExp.fwhm_minor_lq', $buttonLink);
+$buttonLink = button_restrict_string ('camProcessedExp.iq_fwhm_major', $buttonLink);
+$buttonLink = button_restrict_min ('camProcessedExp.iq_fwhm_major', $buttonLink);
+$buttonLink = button_restrict_max ('camProcessedExp.iq_fwhm_major', $buttonLink);
+$buttonLink = button_restrict_string ('camProcessedExp.iq_fwhm_minor', $buttonLink);
+$buttonLink = button_restrict_min ('camProcessedExp.iq_fwhm_minor', $buttonLink);
+$buttonLink = button_restrict_max ('camProcessedExp.iq_fwhm_minor', $buttonLink);
+$buttonLink = button_restrict_string ('camProcessedExp.iq_m2', $buttonLink);
+$buttonLink = button_restrict_min ('camProcessedExp.iq_m2', $buttonLink);
+$buttonLink = button_restrict_max ('camProcessedExp.iq_m2', $buttonLink);
+$buttonLink = button_restrict_string ('camProcessedExp.iq_m3', $buttonLink);
+$buttonLink = button_restrict_min ('camProcessedExp.iq_m3', $buttonLink);
+$buttonLink = button_restrict_max ('camProcessedExp.iq_m3', $buttonLink);
+$buttonLink = button_restrict_string ('camProcessedExp.iq_m4', $buttonLink);
+$buttonLink = button_restrict_min ('camProcessedExp.iq_m4', $buttonLink);
+$buttonLink = button_restrict_max ('camProcessedExp.iq_m4', $buttonLink);
+
+$buttonLink = button_restrict_checkbox ('hist.exp_time', $buttonLink);
+$buttonLink = button_restrict_checkbox ('hist.airmass', $buttonLink);
+$buttonLink = button_restrict_checkbox ('hist.fwhm_major_psf', $buttonLink);
+$buttonLink = button_restrict_checkbox ('hist.fwhm_minor_psf', $buttonLink);
+$buttonLink = button_restrict_checkbox ('hist.fwhm_uq', $buttonLink);
+$buttonLink = button_restrict_checkbox ('hist.fwhm_lq', $buttonLink);
+$buttonLink = button_restrict_checkbox ('hist.fwhm_major_moments', $buttonLink);
+$buttonLink = button_restrict_checkbox ('hist.fwhm_minor_moments', $buttonLink);
+$buttonLink = button_restrict_checkbox ('hist.fwhm_m2_moment', $buttonLink);
+$buttonLink = button_restrict_checkbox ('hist.fwhm_m3_moment', $buttonLink);
+$buttonLink = button_restrict_checkbox ('hist.fwhm_m4_moment', $buttonLink);
+
+$buttonLink = button_restrict_string ('hist_delta.exp_time', $buttonLink);
+$buttonLink = button_restrict_string ('hist_delta.airmass', $buttonLink);
+$buttonLink = button_restrict_string ('hist_delta.fwhm_major_psf', $buttonLink);
+$buttonLink = button_restrict_string ('hist_delta.fwhm_minor_psf', $buttonLink);
+$buttonLink = button_restrict_string ('hist_delta.fwhm_uq', $buttonLink);
+$buttonLink = button_restrict_string ('hist_delta.fwhm_lq', $buttonLink);
+$buttonLink = button_restrict_string ('hist_delta.fwhm_major_moments', $buttonLink);
+$buttonLink = button_restrict_string ('hist_delta.fwhm_minor_moments', $buttonLink);
+$buttonLink = button_restrict_string ('hist_delta.fwhm_m2_moment', $buttonLink);
+$buttonLink = button_restrict_string ('hist_delta.fwhm_m3_moment', $buttonLink);
+$buttonLink = button_restrict_string ('hist_delta.fwhm_m4_moment', $buttonLink);
+
+navigate_buttons ($rowStart, $rowLast, $dTABLE, $rowTotal, $buttonLink, $ID, 'histogram.php');
+
+echo "&nbsp; : &nbsp; or enter start row: <input type=\"text\" name=\"from\" size=\"5\" value=\"$rowStart\">\n";
+
+// set up the table
+echo "<table class=list>\n";
+
+// echo "<tr><td></td>\n"; // first field is a label set below for the query rows only
+// ** TABLE HEADER **
+echo "<tr><td></td>\n";
+write_header_cell ("list", "Exp Name");
+write_header_cell ("list", "Label");
+write_header_cell ("list", "data grp");
+write_header_cell ("list", "dist grp");
+write_header_cell ("list", "Date/Time");
+write_header_cell ("list", "FILTER");
+write_header_cell ("list", "exp_time    ");
+write_header_cell ("list", "airmass     ");
+write_header_cell ("list", "fwhm major psf");
+write_header_cell ("list", "fwhm minor psf");
+write_header_cell ("list", "fwhm UQ");
+write_header_cell ("list", "fwhm LQ");
+write_header_cell ("list", "fwhm major moments");
+write_header_cell ("list", "fwhm minor moments");
+write_header_cell ("list", "m2 moment");
+write_header_cell ("list", "m3 moment");
+write_header_cell ("list", "m4 moment");
+echo "</tr>\n";
+
+// query restriction form
+// echo "<tr>\n";
+// echo "<td class=list> <input type=\"text\" name=\"expID\"></td>\n";
+// ** TABLE QUERY **
+echo "<tr><td>&ge;</td>\n";
+write_query_row ('rawExp.exp_name', 5, 'string');
+write_query_row ('chipRun.label', 7, 'string');
+write_query_row ('chipRun.data_group', 7, 'string');
+write_query_row ('chipRun.dist_group', 7, 'string');
+write_query_row ('rawExp.dateobs', 19, 'min');
+write_query_row ('rawExp.filter', 10, 'string');
+write_query_row ('rawExp.exp_time', 5, 'min');
+write_query_row ('rawExp.airmass', 5, 'min');
+write_query_row ('camProcessedExp.fwhm_major', 5, 'min');
+write_query_row ('camProcessedExp.fwhm_minor', 5, 'min');
+write_query_row ('camProcessedExp.fwhm_major_uq', 5, 'min');
+write_query_row ('camProcessedExp.fwhm_major_lq', 5, 'min');
+write_query_row ('camProcessedExp.iq_fwhm_major', 5, 'min');
+write_query_row ('camProcessedExp.iq_fwhm_minor', 5, 'min');
+write_query_row ('camProcessedExp.iq_m2', 5, 'min');
+write_query_row ('camProcessedExp.iq_m3', 5, 'min');
+write_query_row ('camProcessedExp.iq_m4', 5, 'min');
+echo "</tr><tr><td>&le;</td>\n";
+echo "<td> &nbsp; </td>\n";
+echo "<td> &nbsp; </td>\n";
+echo "<td> &nbsp; </td>\n";
+echo "<td> &nbsp; </td>\n";
+write_query_row ('rawExp.dateobs', 19, 'max');
+echo "<td> &nbsp; </td>\n";
+write_query_row ('rawExp.exp_time', 5, 'max');
+write_query_row ('rawExp.airmass', 5, 'max');
+write_query_row ('camProcessedExp.fwhm_major', 5, 'max');
+write_query_row ('camProcessedExp.fwhm_minor', 5, 'max');
+write_query_row ('camProcessedExp.fwhm_major_uq', 5, 'max');
+write_query_row ('camProcessedExp.fwhm_major_lq', 5, 'max');
+write_query_row ('camProcessedExp.iq_fwhm_major', 5, 'max');
+write_query_row ('camProcessedExp.iq_fwhm_minor', 5, 'max');
+write_query_row ('camProcessedExp.iq_m2', 5, 'max');
+write_query_row ('camProcessedExp.iq_m3', 5, 'max');
+write_query_row ('camProcessedExp.iq_m4', 5, 'max');
+echo "</tr>\n";
+// echo "</tr>\n";
+
+//checkboxes
+echo "<tr><td></td>\n";
+echo "<td> &nbsp; </td>\n";
+echo "<td> &nbsp; </td>\n";
+echo "<td> &nbsp; </td>\n";
+echo "<td> &nbsp; </td>\n";
+echo "<td> &nbsp; </td>\n";//dateobs
+echo "<td> &nbsp; </td>\n";
+write_query_checkbox ('hist.exp_time', 'Histogram?');
+write_query_checkbox ('hist.airmass', 'Histogram?');
+write_query_checkbox ('hist.fwhm_major_psf', 'Histogram?');
+write_query_checkbox ('hist.fwhm_minor_psf', 'Histogram?');
+write_query_checkbox ('hist.fwhm_uq', 'Histogram?');
+write_query_checkbox ('hist.fwhm_lq', 'Histogram?');
+write_query_checkbox ('hist.fwhm_major_moments', 'Histogram?');
+write_query_checkbox ('hist.fwhm_minor_moments', 'Histogram?');
+write_query_checkbox ('hist.fwhm_m2_moment', 'Histogram?');
+write_query_checkbox ('hist.fwhm_m3_moment', 'Histogram?');
+write_query_checkbox ('hist.fwhm_m4_moment', 'Histogram?');
+echo "</tr>\n";
+
+echo "<tr><td></td>\n";
+echo "<td> &nbsp; </td>\n";
+echo "<td> &nbsp; </td>\n";
+echo "<td> &nbsp; </td>\n";
+echo "<td> &nbsp; </td>\n";
+echo "<td> &nbsp; </td>\n";
+echo "<td> Bin size (delta)</td>\n";
+write_query_row ('hist.exp_time_delta', 5, 'string');
+write_query_row ('hist.airmass_delta', 5, 'string');
+write_query_row ('hist.fwhm_major_psf_delta', 5, 'string');
+write_query_row ('hist.fwhm_minor_psf_delta', 5, 'string');
+write_query_row ('hist.fwhm_uq_delta', 5, 'string');
+write_query_row ('hist.fwhm_lq_delta', 5, 'string');
+write_query_row ('hist.fwhm_major_moments_delta', 5, 'string');
+write_query_row ('hist.fwhm_minor_moments_delta', 5, 'string');
+write_query_row ('hist.fwhm_m2_moment_delta', 5, 'string');
+write_query_row ('hist.fwhm_m3_moment_delta', 5, 'string');
+write_query_row ('hist.fwhm_m4_moment_delta', 5, 'string');
+echo "</tr>\n";
+
+echo "<tr><td></td>\n";
+echo "<td> &nbsp; </td>\n";
+echo "<td> &nbsp; </td>\n";
+echo "<td> &nbsp; </td>\n";
+echo "<td> &nbsp; </td>\n";
+echo "<td> &nbsp; </td>\n";
+echo "<td> Bins number (default: 100)</td>\n";
+write_query_row ('hist.exp_time_count', 5, 'string');
+write_query_row ('hist.airmass_count', 5, 'string');
+write_query_row ('hist.fwhm_major_psf_count', 5, 'string');
+write_query_row ('hist.fwhm_minor_psf_count', 5, 'string');
+write_query_row ('hist.fwhm_uq_count', 5, 'string');
+write_query_row ('hist.fwhm_lq_count', 5, 'string');
+write_query_row ('hist.fwhm_major_moments_count', 5, 'string');
+write_query_row ('hist.fwhm_minor_moments_count', 5, 'string');
+write_query_row ('hist.fwhm_m2_moment_count', 5, 'string');
+write_query_row ('hist.fwhm_m3_moment_count', 5, 'string');
+write_query_row ('hist.fwhm_m4_moment_count', 5, 'string');
+echo "</tr>\n";
+
+// close the table and form
+echo "</table>\n";
+echo "<a href=\"http://en.wikipedia.org/wiki/Histogram#Number_of_bins_and_width\">About the \"good\" number of bins<br><br></a>\n";
+echo "<input type=\"submit\" name=\"constraints\" value=\"refine search/show histograms\">\n";
+$pass = $ID['pass'];
+$proj = $ID['proj'];
+$menu = $ID['menu'];
+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";
+
+$keysToRowIndex = array( 
+      'hist.exp_time' => 6,
+      'hist.airmass' => 7,
+      'hist.fwhm_major_psf' => 8,
+      'hist.fwhm_minor_psf' => 9,
+      'hist.fwhm_uq'=> 10,
+      'hist.fwhm_lq' => 11,
+      'hist.fwhm_major_moments' => 12,
+      'hist.fwhm_minor_moments' => 13,
+      'hist.fwhm_m2_moment' => 14,
+      'hist.fwhm_m3_moment' => 15,
+      'hist.fwhm_m4_moment' =>16);
+$handles = array();
+$filenames = array();
+foreach ($keysToRowIndex as $i => $value) {
+    if (get_value_from_key($i, TRUE) == "on") {
+       $filenames[$i] = tempnam("/tmp/serge", "histodat.");
+       if ($debug) {
+          echo "<b>Using $filenames[$i] for $i</b>\n";
+       }
+       $handles[$i] = fopen($filenames[$i], "w");
+    }
+}
+// ** TAIL CODE **
+while ($qry->fetchInto($row)) {
+  foreach ($keysToRowIndex as $i => $value) {
+    if (get_value_from_key($i, TRUE) == "on") {
+      fwrite($handles[$i],
+	sprintf("%.3f\n", $row[$value]));
+    }
+  }
+}
+foreach ($keysToRowIndex as $i => $value) {
+  if ($handles[$i] != NULL) {
+    fclose($handles[$i]);
+    chmod($filenames[$i], 0777);
+  }
+}
+
+if ($debug) {
+   $show = get_value_from_key('hist.exp_time', TRUE);
+   $show .= " && ".get_value_from_key('hist.airmass', TRUE);
+   $show .= " && ".get_value_from_key('hist.fwhm_major_psf', TRUE);
+   $show .= " && ".get_value_from_key('hist.fwhm_minor_psf', TRUE);
+   $show .= " && ".get_value_from_key('hist.fwhm_uq', TRUE);
+   $show .= " && ".get_value_from_key('hist.fwhm_lq', TRUE);
+   $show .= " && ".get_value_from_key('hist.fwhm_major_moments', TRUE);
+   $show .= " && ".get_value_from_key('hist.fwhm_minor_moments', TRUE);
+   $show .= " && ".get_value_from_key('hist.fwhm_m2_moment', TRUE);
+   $show .= " && ".get_value_from_key('hist.fwhm_m3_moment', TRUE);
+   $show .= " && ".get_value_from_key('hist.fwhm_m4_moment', TRUE);
+   echo "==&gt; ($show)<br>\n";
+}
+
+//Get environment
+$PATH = getenv("PATH");
+putenv("PATH=$BINDIR:$PATH");
+$LD_LIBRARY_PATH = getenv("LD_LIBRARY_PATH");
+putenv("LD_LIBRARY_PATH=$LIBDIR:$LD_LIBRARY_PATH");
+
+echo "<br>\n";
+foreach ($filenames as $i => $value) {
+  $outfile = $value.".png";
+  $deltaValue = get_value_from_key($i."_delta", TRUE);
+  $binValue = get_value_from_key($i."_count", TRUE);
+  if ($binValue == "") {
+     $binValue = "-1";
+     if ($deltaValue == "") {
+       $binValue = "100";
+       $deltaValue = "-1";
+     }    
+  } else {
+    if ($deltaValue != "") {
+      echo "<font color=red>Warning! Both 'Bins number' and 'delta value' are specified =&gt; 'delta value' will be ignored<br></font>\n";
+      $deltaValue = "-1";
+    } else {
+      $deltaValue = "-1";
+    }
+  }
+  $title = preg_replace ('|hist\.|', '', $i);
+  $title = preg_replace ('|_|', '-', $title);
+
+  echo "Showing histogram for [$title] /  delta = $deltaValue / bins = $binValue<br>\n";
+  exec("build_histogram.dvo $value $outfile $deltaValue $binValue $title",
+  	$output, $status);
+  if ($debug) {
+    echo "&nbsp;&nbsp;&nbsp;&nbsp;Status = $status<br>\n";
+    for ($i = 0; $i < count($output); $i++) {
+      echo "&nbsp;&nbsp;&nbsp;&nbsp;output $i: $output[$i]<br>";
+    }
+  }
+  $blah=count($output);
+  if ($debug) {
+    echo "&nbsp;&nbsp;&nbsp;&nbsp;end: $output / $blah<br>\n";
+  }
+  echo "<img src=\"show_and_delete_image.php?file=$outfile\"><br><br>\n";
+  unset($output);
+  $filesToRemove[$value] = '';
+}
+
+echo "<small> WHERE: $WHERE<br><br></small>\n";
+echo "<small> SQL: $sql<br><br></small>\n";
+
+if ($debug) {
+  echo "Cleaning<br>\n";
+}
+foreach ($filesToRemove as $index => $value) {
+  if ($debug) {
+    echo "Deleting $index<br>\n";
+  }
+  unlink($index);
+}
+if ($debug) {
+  echo "Cleaning end<br>\n";
+}
+
+menu_end();
+
+?>
Index: /branches/pap/ippMonitor/raw/ipp.imfiles.dat
===================================================================
--- /branches/pap/ippMonitor/raw/ipp.imfiles.dat	(revision 28483)
+++ /branches/pap/ippMonitor/raw/ipp.imfiles.dat	(revision 28484)
@@ -22,3 +22,4 @@
 menutop   | menutop      | link    | simple plot - cam            | simplePlotcam.php
 menutop   | menutop      | link    | czartool                     | czartool_labels.php
-
+menutop   | menutop      | link    | mask stats                   | maskStats.php
+menutop   | menutop      | link    | histogram                    | histogram.php
Index: /branches/pap/ippMonitor/raw/ipp.load.dat
===================================================================
--- /branches/pap/ippMonitor/raw/ipp.load.dat	(revision 28483)
+++ /branches/pap/ippMonitor/raw/ipp.load.dat	(revision 28484)
@@ -2,4 +2,5 @@
 
 menutop   | menutop      | plain   | &nbsp;                       | 
+menulink  | menuselect 	 | link    | Raw Exp Summary              | rawSummary.php             
 menulink  | menuselect 	 | link    | Raw Exposures                | rawExp.php             
 menulink  | menuselect 	 | link    | Raw Detrend Exp              | rawDetrendExp.php             
Index: /branches/pap/ippMonitor/raw/ipp.php
===================================================================
--- /branches/pap/ippMonitor/raw/ipp.php	(revision 28483)
+++ /branches/pap/ippMonitor/raw/ipp.php	(revision 28484)
@@ -564,6 +564,41 @@
 }
 
+// 
+function button_restrict_checkbox ($key, $line) {
+  $htmlkey = preg_replace ('|\.|', '_', $key);
+  if ($_SERVER[REQUEST_METHOD] == 'GET') { 
+    $value = $_GET[$htmlkey]; 
+  } else {
+    $value = $_POST[$htmlkey];
+  }
+  if ($value != "") {
+    if ($line) {
+      $line = $line . "&$htmlkey=$value";
+    } else {
+      $line = "$htmlkey=$value";
+    }
+  }
+  return $line;
+}
+
+// 
+function button_restrict_radio ($key, $line) {
+  $htmlkey = preg_replace ('|\.|', '_', $key);
+  if ($_SERVER[REQUEST_METHOD] == 'GET') { 
+    $value = $_GET[$htmlkey]; 
+  } else {
+    $value = $_POST[$htmlkey];
+  }
+  if ($value != "") {
+    if ($line) {
+      $line = $line . "&$htmlkey=$value";
+    } else {
+      $line = "$htmlkey=$value";
+    }
+  }
+  return $line;
+}
+
 function write_header_cell ($class, $name) {
-
   echo "<th class=\"$class\">$name</th>\n";
 }
@@ -637,6 +672,45 @@
   if ($value != "") { 
     echo "value=\"$value\">";
-  } 
+  } else {
+    echo ">";
+  }
   echo "</td>\n";
+}
+
+// checkbox
+function write_query_checkbox ($key, $comment) {
+  $htmlkey = preg_replace ('|\.|', '_', $key);
+
+  if ($_SERVER[REQUEST_METHOD] == 'GET') { 
+    $value = $_GET[$htmlkey]; 
+  } else {
+    $value = $_POST[$htmlkey];
+  }
+  echo "<td> <input type=\"checkbox\" name=\"$htmlkey\"";
+  if ($value == "on") { 
+    echo " checked>";
+  } else {
+    echo ">";
+  }
+  echo "$comment ($htmlkey/$value)</td>\n";
+}
+
+// checkbox
+function write_query_radio ($value, $key) {
+  $htmlkey = preg_replace ('| |', '_', $key);
+  $htmlvalue = preg_replace ('|\.|', '_', $value);
+
+  if ($_SERVER[REQUEST_METHOD] == 'GET') { 
+    $value = $_GET[$htmlkey]; 
+  } else {
+    $value = $_POST[$htmlkey];
+  }
+  if ($htmlvalue == get_value_from_key($htmlkey, TRUE)) {
+    $checked = "checked";
+  } else {
+    $checked = "";
+  }
+  echo "<td> <input type=\"radio\" name=\"$htmlkey\" value=\"$htmlvalue\" $checked/>";
+  echo "$key ($htmlkey/$htmlvalue)</td>\n";
 }
 
@@ -778,3 +852,22 @@
 // $myPage = $_SERVER[SCRIPT_NAME] . "?pass=$pass";
 
+//////////////////////////////////////////////////////////////////////////
+// Return the value associated to the key (GET or POST method)
+// If convertKeyToHtmlkey is true, the key is converted to a 
+// so-called HTML, i.e. the key where '.' are replaced by '_'
+// (e.g. the conversion of 'f.o.o.b_a_r' is 'f_o_o_b_a_r'.
+//
+function get_value_from_key ($key, $convertKeyToHtmlkey) {
+  if ($convertKeyToHtmlkey) {
+    $htmlkey = preg_replace ('|\.|', '_', $key);
+  } else {
+    $htmlkey = $key;
+  }
+  if ($_SERVER[REQUEST_METHOD] == 'GET') { 
+    return $_GET[$htmlkey]; 
+  } else {
+    return $_POST[$htmlkey]; 
+  }
+}
+
 ?>
Index: /branches/pap/ippMonitor/raw/show_and_delete_image.php
===================================================================
--- /branches/pap/ippMonitor/raw/show_and_delete_image.php	(revision 28484)
+++ /branches/pap/ippMonitor/raw/show_and_delete_image.php	(revision 28484)
@@ -0,0 +1,18 @@
+<?php
+
+### we must have been past arguments with GET:
+if ($_SERVER[REQUEST_METHOD] != 'GET') { 
+  exit ();
+}
+
+$filename = $_GET[file];
+
+$file = fopen ($filename, "r");
+if ($file && !$debug) {
+  header ('Content-Type: image/png');
+  fpassthru ($file);
+}
+
+unlink($filename);
+exit ();
+?>
Index: /branches/pap/ippMonitor/scripts/build_histogram.dvo
===================================================================
--- /branches/pap/ippMonitor/scripts/build_histogram.dvo	(revision 28484)
+++ /branches/pap/ippMonitor/scripts/build_histogram.dvo	(revision 28484)
@@ -0,0 +1,78 @@
+#!/usr/bin/env dvo
+
+$debug = 0
+
+$bins = 100
+$delta = -1
+
+macro build_histogram
+  myecho "Loading data from = $1"
+  data $1
+  read v 1
+  minimum v
+  maximum v
+  myecho "minvalue = $minvalue"
+  myecho "maxvalue = $maxvalue"
+
+  if ($bins >= 2)
+    $delta = ($maxvalue-$minvalue)/$bins
+    myecho "delta = $delta"
+  else
+    if ($delta <= 0)
+      $bins = 100
+      $delta = ($maxvalue-$minvalue)/$bins
+      myecho "delta = $delta"
+    end
+  end
+
+  histo v vhist $minvalue $maxvalue $delta -range dx
+  myecho "bins = dx[]"
+  myecho "  Setting limits"
+  limits dx vhist
+  plot dx vhist -x 1
+  clear
+  myecho "  Showing box"
+  box
+  myecho "  Plotting"
+  plot dx vhist -x 1 -c blue
+  $binsCount = dx[]
+  myecho "  Labeling"
+  myecho "    -x $3"
+  myecho "    -y Occurrences "
+  myecho "    +x 'Histogram of $3 "
+  myecho "           interval = $minvalue : $maxvalue "
+  myecho "	     / $binsCount bins"
+  myecho "           / bin width: $delta)'"
+  labels -x $3
+  labels -y Occurrences
+  labels +x "Histogram of $3 / interval = $minvalue : $maxvalue / $binsCount bins / bin width: $delta"
+
+  myecho "Saving graphics to [$2]"
+  png -name $2
+end
+
+if ($SCRIPT)
+  $KAPA = kapa -noX
+  resize 1000 1000
+  if ($argv:n != 5)
+    echo "USAGE: build_histogram (input) (output) (delta) (bins) (x-axis label)"
+    echo "  If (bins) >= 2"
+    echo "     build histogram with (bins) bins ((delta) is ignored)"
+    echo "  Otherwise"
+    echo "     if delta > 0
+    echo "        build histogram with (delta)-wide bins"
+    echo "     otherwise"
+    echo "        build histogram with 100 bins"
+    exit 1
+  end
+
+  echo "Loading dependencies"
+  $helpersFilename = `which helpers.dvo`
+  input $helpersFilename
+  echo "Dependencies loaded"
+
+  $bins = $argv:3 + 1
+  $delta = $argv:2
+  build_histogram $argv:0 $argv:1 $argv:4
+  exit 0
+end
Index: /branches/pap/ippMonitor/scripts/czartool_checkServer.pl
===================================================================
--- /branches/pap/ippMonitor/scripts/czartool_checkServer.pl	(revision 28483)
+++ /branches/pap/ippMonitor/scripts/czartool_checkServer.pl	(revision 28484)
@@ -17,5 +17,5 @@
     if ($line =~ m/Scheduler is stopped/) {$isRunning = 0; $foundStatus=1;last;}
     if ($line =~ m/Scheduler is running/) {$isRunning = 1; $foundStatus=1;last;}
-    if ($line =~ m/Task Staus/) {last;}
+    if ($line =~ m/Task Status/) {last;}
 
 }
Index: /branches/pap/ippScripts/Build.PL
===================================================================
--- /branches/pap/ippScripts/Build.PL	(revision 28483)
+++ /branches/pap/ippScripts/Build.PL	(revision 28484)
@@ -62,4 +62,6 @@
         scripts/magic_destreak_cleanup.pl
         scripts/magic_destreak_defineruns.pl
+        scripts/minidvodb_createdb.pl
+        scripts/minidvodb_merge.pl
         scripts/ippdb.pl
         scripts/ipp_cleanup.pl
@@ -109,4 +111,6 @@
         scripts/background_chip.pl
         scripts/background_warp.pl
+        scripts/skycell_jpeg.pl
+        scripts/diffphot.pl
     )],
     dist_abstract => 'Scripts for running the Pan-STARRS IPP',
Index: /branches/pap/ippScripts/MANIFEST
===================================================================
--- /branches/pap/ippScripts/MANIFEST	(revision 28483)
+++ /branches/pap/ippScripts/MANIFEST	(revision 28484)
@@ -42,3 +42,4 @@
 scripts/bundle_detrends.pl
 scripts/ipp_cluster_load_monitor.pl
+scripts/skycell_jpeg.pl
 t/00_distribution.t
Index: /branches/pap/ippScripts/scripts/addstar_run.pl
===================================================================
--- /branches/pap/ippScripts/scripts/addstar_run.pl	(revision 28483)
+++ /branches/pap/ippScripts/scripts/addstar_run.pl	(revision 28484)
@@ -36,6 +36,6 @@
     exit($PS_EXIT_CONFIG_ERROR);
 }
-
-my ( $exp_tag, $add_id, $camera, $outroot, $camroot, $dbname, $reduction, $dvodb, $image_only, $verbose, $no_update,
+my $minidvodb_path;
+my ( $exp_tag, $add_id, $camera, $outroot, $camroot, $dbname, $reduction, $dvodb, $minidvodb, $minidvodb_name, $minidvodb_group, $image_only, $verbose, $no_update,
      $no_op, $redirect, $save_temps);
 GetOptions(
@@ -48,4 +48,7 @@
     'reduction=s'       => \$reduction, # Reduction class
     'dvodb|w=s'         => \$dvodb,  # output DVO database
+    'minidvodb_name|w=s'=> \$minidvodb_name,  # miniDVO database name
+    'minidvodb_group|w=s' => \$minidvodb_group, # miniDVO database group
+    'minidvodb'         => \$minidvodb,  # use minidvodb?
     'image-only'        => \$image_only,   # Print to stdout
     'verbose'           => \$verbose,   # Print to stdout
@@ -58,5 +61,5 @@
 pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
 pod2usage(
-          -msg => "Required options: --exp_tag --add_id --camera --outroot",
+          -msg => "Required options: --exp_tag --add_id --camera --outroot --dvodb --camroot",
           -exitval => 3,
           ) unless
@@ -65,6 +68,9 @@
     defined $outroot and
     defined $camroot and
+    defined $dvodb and
     defined $camera;
-
+if ($minidvodb && !defined($minidvodb_group)) {
+		my_die( "missing minidvodb_group", $add_id, 3 );
+	    }
 my $ipprc = PS::IPP::Config->new( $camera ) or my_die( "Unable to set up", $add_id, $PS_EXIT_CONFIG_ERROR ); # IPP configuration
 
@@ -98,11 +104,49 @@
 if (defined $dvodb) {
     $dvodbReal = $ipprc->dvo_catdir( $dvodb ); # catdir for DVO
-    $dvodbReal = $ipprc->convert_filename_absolute( $dvodbReal );
-}
+    $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 .= " -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	   
+	    
+	}
 unless ($no_op) {
-    if (defined $dvodbReal) {
+   	    print $dvodbReal;
 
         ## addstar can either save the full set of detections, or just
@@ -120,5 +164,5 @@
         my $command  = "$addstar -update"; # XXX optionally set -update?
         $command .= " -D CAMERA $camdir";
-        $command .= " -D CATDIR $dvodbReal";
+        $command .= " -D CATDIR $minidvodb_path";
         $command .= " $realFile";
         $command .= " -use-name $fpaObjects"; # DVO wants the neb-name as a file reference
@@ -142,6 +186,8 @@
 $fpaCommand .= " -path_base $outroot";
 $fpaCommand .= " -dtime_addstar $dtime_addstar";
+$fpaCommand .= " -dvodb_path $minidvodb_path" if defined $minidvodb_path;
+$fpaCommand .= " -minidvodb_name $minidvodb_name" if defined $minidvodb_name;
 $fpaCommand .= " -dbname $dbname" if defined $dbname;
-
+print $fpaCommand;
 # Add the result into the database
 unless ($no_update) {
@@ -171,6 +217,8 @@
         $command .= " -addprocessedexp";
         $command .= " -fault $exit_code";
+	$command .= " -dvodb_path $minidvodb_path" if defined $minidvodb_path;
         $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 .= " -dbname $dbname" if defined $dbname;
         system ($command);
Index: /branches/pap/ippScripts/scripts/automate_stacks.pl
===================================================================
--- /branches/pap/ippScripts/scripts/automate_stacks.pl	(revision 28483)
+++ /branches/pap/ippScripts/scripts/automate_stacks.pl	(revision 28484)
@@ -17,7 +17,9 @@
 my $missing_tools = 0;
 my $chiptool = can_run('chiptool') or (warn "Can't find chiptool" and $missing_tools = 1);
+my $dqstatstool = can_run('dqstatstool') or (warn "Can't find dqstatstool" and $missing_tools = 1);
 my $warptool = can_run('warptool') or (warn "Can't find warptool" and $missing_tools = 1);
 my $stacktool= can_run('stacktool') or (warn "Can't find stacktool" and $missing_tools = 1);
 my $difftool = can_run('difftool') or (warn "Can't find difftool" and $missing_tools = 1);
+my $dettool = can_run('dettool') or (warn "Can't find dettool" and $missing_tools = 1);
 my $ppConfigDump = can_run('ppConfigDump') or (warn "Can't find ppConfigDump" and $missing_tools = 1);
 my $mkBTpcontrol = can_run('make_burntool_pcontrol.pl') or (warn "Can't find make_burntool_pcontrol.pl" and $missing_tools = 1);
@@ -27,22 +29,4 @@
     exit($PS_EXIT_CONFIG_ERROR);
 }
-
-# my @filter_list = ('g.00000','r.00000','i.00000','z.00000','y.00000');
-# my @target_list = ('CMB','M31','MD01','MD02','MD03','MD04','MD05','MD06','MD07','MD08','MD09','MD10',
-#                  'STS','SVS','SweetSpot','ThreePi');
-# my %tessID_list = ('CMB' => 'RINGS.V0', 'M31' => 'M31', 'MD01' => 'MD01', 'MD02' => 'MD02',
-#                  'MD03' => 'MD03', 'MD04' => 'MD04', 'MD05' => 'MD05', 'MD06' => 'MD06',
-#                  'MD07' => 'MD07', 'MD08' => 'MD08', 'MD09' => 'MD09', 'MD10' => 'MD10',
-#                  'STS' => 'STS', 'SVS' => 'RINGS.V0', 'SweetSpot' => 'RINGS.V0', 'ThreePi' => 'RINGS.V0');
-# my %comment_list = ('CMB' => 'CMB_Cold%', 'M31' => 'M31%', 'MD01' => 'MD01%', 'MD02' => 'MD02%',
-#                   'MD03' => 'MD03%', 'MD04' => 'MD04%', 'MD05' => 'MD05%', 'MD06' => 'MD06%',
-#                   'MD07' => 'MD07%', 'MD08' => 'MD08%', 'MD09' => 'MD09%', 'MD10' => 'MD10%',
-#                   'STS' => 'Stellar Transit%', 'SVS' => 'SVS%', 'SweetSpot' => 'Sweetspot%', 'ThreePi' => 'ThreePi%');
-# my %stackable_list = ('CMB' => 0, 'M31' => 1, 'MD01' => 1, 'MD02' => 1,
-#                   'MD03' => 1, 'MD04' => 1, 'MD05' => 1, 'MD06' => 1,
-#                   'MD07' => 1, 'MD08' => 1, 'MD09' => 1, 'MD10' => 1,
-#                   'STS' => 1, 'SVS' => 0, 'SweetSpot' => 0, 'ThreePi' => 0);
-# my $retention_time = 9000;  # days.
-
 
 my $db;
@@ -59,4 +43,5 @@
 my ( $check_registration, $define_burntool, $queue_burntool, $check_chips, $queue_chips);
 my ( $check_stacks, $queue_stacks, $check_diffs, $queue_diffs, $clean_old);
+my ( $check_detrends, $queue_detrends, $check_dqstats, $queue_dqstats);
 
 GetOptions(
@@ -81,4 +66,8 @@
     'check_stacks'         => \$check_stacks,
     'queue_stacks'         => \$queue_stacks,
+    'check_detrends'       => \$check_detrends,
+    'queue_detrends'       => \$queue_detrends,
+    'check_dqstats'        => \$check_dqstats,
+    'queue_dqstats'        => \$queue_dqstats,
     'check_diffs'          => \$check_diffs,
     'queue_diffs'          => \$queue_diffs,
@@ -105,4 +94,8 @@
            --check_stacks         Confirm that stacks can be built.
            --queue_stacks         Issue stacktool commands to queue stacks.
+           --check_detrends       Confirm that detrend verify runs can be built.
+           --queue_detrends       Issue dettool commands to queue detrend verify runs.
+           --check_dqstats        Confirm that dqstats tables can be built.
+           --queue_dqstats        Issue dqstatstool commands to queue dqstat tables.
            --check_diffs          Confirm that diffs can be done.
            --queue_diffs          Issue difftool commands to queue diffs.
@@ -115,6 +108,6 @@
           ) unless
     defined $check_registration or defined $define_burntool or defined $queue_burntool or
-    defined $queue_chips or defined $queue_stacks or
-    defined $check_chips or defined $check_stacks or
+    defined $queue_chips or defined $queue_stacks or $queue_detrends or $queue_dqstats or
+    defined $check_chips or defined $check_stacks or $check_detrends or $check_dqstats or
     defined $test_mode or defined $clean_old or defined $check_mode;
 
@@ -126,9 +119,18 @@
 my %object_list = ();
 my %comment_list= ();
+my %cleanmods_list = ();
 my %stackable_list = ();
 my %reduction_class = ();
+my @detrend_list = ();
+my %dettype_list = ();
+my %exptype_list = ();
+my %refID_list = ();
+my %refIter_list = ();
+my %detfilter_list = ();
+my %detmax_list = ();
 my %clean_commands = ();
 my %clean_retention = ();
 my %noclean_list = ();
+my %clean_alternate = ();
 # Grab the configuration data.
 my $conf_cmd = "$ppConfigDump -dump-recipe NIGHTLY_SCIENCE -";
@@ -137,5 +139,5 @@
 unless ($success) {
     $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
-    &my_die("Unable to perform ppConfigDUmp: $error_code", $date, $PS_EXIT_SYS_ERROR);
+    &my_die("Unable to perform ppConfigDump: $error_code", $date, $PS_EXIT_SYS_ERROR);
 }
 
@@ -156,9 +158,9 @@
 		$clean_retention{$this_mode} = ${ $mentry }{value};
 	    }
+	    elsif (${ $mentry }{name} eq 'ALTERNATE_CMD') {
+		$clean_alternate{$this_mode} = ${ $mentry }{value};
+	    }
 	}
     }
-#    if (${ $entry }{name} eq 'RETENTION_TIME') {
-#        $retention_time = ${ $entry }{value};
-#    }
     elsif (${ $entry }{name} eq 'FILTERS') {
         push @filter_list, ${ $entry }{value};
@@ -193,10 +195,40 @@
 		$noclean_list{$this_target} = ${ $tentry }{value};
 	    }
-        }
-    }
-}
-
-
-
+	    else {
+		if (exists($clean_commands{ ${ $tentry }{name} })) {
+		    $cleanmods_list{$this_target}{${ $tentry }{name} } = ${ $tentry }{value};
+		}
+	    }
+        }
+    }
+    elsif (${ $entry }{name} eq 'DETRENDS') {
+	my @detrend_data = @{ ${ $entry }{value} };
+	my $this_detrend = '';
+	foreach my $dentry (@detrend_data) {
+	    if (${ $dentry }{name} eq 'NAME') {
+		$this_detrend = ${ $dentry }{value};
+		push @detrend_list, $this_detrend;
+	    }
+	    elsif (${ $dentry }{name} eq 'DETTYPE') {
+		$dettype_list{$this_detrend} = ${ $dentry }{value};
+	    }
+	    elsif (${ $dentry }{name} eq 'EXPTYPE') {
+		$exptype_list{$this_detrend} = ${ $dentry }{value};
+	    }
+	    elsif (${ $dentry }{name} eq 'REF_ID') {
+		$refID_list{$this_detrend} = ${ $dentry }{value};
+	    }
+	    elsif (${ $dentry }{name} eq 'REF_ITER') {
+		$refIter_list{$this_detrend} = ${ $dentry }{value};
+	    }
+	    elsif (${ $dentry }{name} eq 'FILTER') {
+		$detfilter_list{$this_detrend} = ${ $dentry }{value};
+	    }
+	    elsif (${ $dentry }{name} eq 'MAX') {
+		$detmax_list{$this_detrend} = ${ $dentry }{value};
+	    }
+	}
+    }		
+}
 
 unless(defined($date)) {
@@ -257,4 +289,28 @@
     unless (defined($test_mode) || defined($check_mode)) { exit(0); }
 }
+if (defined($check_dqstats) || defined($test_mode)) {
+    $metadata_out{nsState} = 'CHECKDQSTATS';
+    &execute_dqstats($date,"pretend");
+    return_metadata($date);
+    unless (defined($test_mode) || defined($check_mode)) { exit(0); }
+}
+if (defined($queue_dqstats) || defined($test_mode)) {
+    $metadata_out{nsState} = 'QUEUEDQSTATS';
+    &execute_dqstats($date);
+    return_metadata($date);
+    unless (defined($test_mode)) { exit(0); }
+}
+if (defined($check_detrends) || defined($test_mode) || defined($check_mode)) {
+    $metadata_out{nsState} = 'CHECKDETRENDS';
+    &execute_detrends($date,"pretend");
+    return_metadata($date);
+    unless (defined($test_mode) || defined($check_mode)) { exit(0); }
+}
+if (defined($queue_detrends)) {
+    $metadata_out{nsState} = 'QUEUEDETRENDS';
+    &execute_detrends($date);
+    return_metadata($date);
+    exit(0);
+}
 if (defined($define_burntool) || defined($test_mode)) {
     $metadata_out{nsState} = 'QUEUEBURNING';
@@ -347,5 +403,5 @@
         if ($summit_fault) {
 	    print STDERR "check_summit_copy: $date $exp_name has summit_fault $summit_fault";
-            if ($exp_type ne 'OBJECT') {
+            if (($exp_type ne 'OBJECT')||($exp_name =~ /^c.*/)) {
                 print STDERR " (but I don't care).\n";
             }
@@ -357,5 +413,5 @@
         elsif (!$download_state or $download_state eq 'run') {
             print STDERR "check_summit_copy: $date $exp_name has download_state $download_state";
-            if ($exp_type ne 'OBJECT') {
+            if (($exp_type ne 'OBJECT')||($exp_name =~ /^c.*/)) {
                 print STDERR " (but I don't care).\n";
             }
@@ -367,5 +423,5 @@
         elsif (!$new_state or $new_state eq 'run' ) {
             print STDERR "check_summit_copy: $date $exp_name has new_state $new_state";
-            if ($exp_type ne 'OBJECT') {
+            if (($exp_type ne 'OBJECT')||($exp_name =~ /^c.*/)) {
                 print STDERR " (but I don't care).\n";
             }
@@ -498,5 +554,5 @@
 
     my $cmd = "$chiptool";
-    $cmd .= ' -simple -dbname gpc1 -definebyquery -set_end_stage warp ';
+    $cmd .= " -simple -dbname $dbname -definebyquery -set_end_stage warp ";
     $cmd .= " -set_label $label ";
     $cmd .= " -set_workdir $workdir -set_dist_group $dist_group ";
@@ -612,4 +668,180 @@
 
 #
+# DQstats
+################################################################################
+
+sub construct_dqstats_cmd {
+    my $date = shift;
+
+    my $select = "-dateobs_end ${date}T23:59:59 ";
+
+    my $cmd = "$dqstatstool";
+    $cmd .= " -simple -dbname $dbname -definebyquery ";
+    $cmd .= " $select ";
+    $cmd .= " -label %.nightlyscience ";
+    $cmd .= " -set_label dqstats.nightlyscience ";
+    if ($debug == 1) {
+	$cmd .= ' -pretend ';
+    }
+    print STDERR "$cmd\n";
+    return($cmd);
+}
+
+sub pre_dqstats_queue {
+    my $date = shift;
+    
+#     my $db = init_gpc_db();
+#     my $trunc_date = $date; $trunc_date =~ s/-//g;
+
+#     my $where = " label LIKE '%.nightlyscience' AND data_group' ";
+#     my $chip_sth = "SELECT * from chipRun WHERE (state = 'full' OR state = 'new' OR state = 'cleaned') AND $where ";
+#     my $cam_sth = "SELECT * from camRun WHERE state = 'full' AND $where ";
+
+#     my $chip_ref = $db->selectall_arrayref( $chip_sth );
+#     my $cam_ref = $db->selectall_arrayref( $cam_sth );
+    
+    my $command = construct_dqstats_cmd($date) . ' -pretend ';
+    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 dqstatstool: $error_code",1,1,,$date, $PS_EXIT_SYS_ERROR);
+    }
+    
+    my @input_exposures = split /\n/, (join '', @$stdout_buf);
+
+    return($#input_exposures + 1,1,1); # $#{ $chip_ref } + 1, $#{ $cam_ref } + 1);
+}
+ 
+sub dqstats_queue {
+    my $date = shift;
+
+    my $command = construct_dqstats_cmd($date);
+    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 dqstatstool: $error_code", 0,0,$date, $PS_EXIT_SYS_ERROR);
+    }
+    return(0);
+
+}
+sub execute_dqstats {
+    my $date = shift;
+    my $pretend = shift;
+    my ($Nexposures,$Nchips,$Ncams) = pre_dqstats_queue($date);
+    if ($Nexposures == 0) {
+	print STDERR "execute_dqstats: No exposures on date $date.\n";
+    }
+    elsif ($Ncams != $Nchips) {
+	print STDERR "execute_dqstats: Not done processing data through camera stage.\n";
+    }
+    else {
+	unless(defined($pretend)) {
+	    dqstats_queue($date);
+	}
+    }
+}   
+#
+# Detrend verification
+################################################################################
+
+sub construct_dettool_cmd {
+    my $date = shift;
+    my $target = shift;
+
+    my ($label,$workdir,$filter,$exp_type,$det_type,$ref_det_id,$ref_iter,$maxN) = get_dettool_parameters($date,$target);
+    
+    my $select = "-select_dateobs_begin ${date}T00:00:00 -select_dateobs_end ${date}T23:59:59 ";
+    $date =~ s/-//g;
+
+    my $cmd = "$dettool";
+#    $cmd .= " -pretend ";
+    $cmd .= " -simple -dbname $dbname -definebyquery -det_type $det_type ";
+    $cmd .= " -mode verify -ref_det_id $ref_det_id -ref_iter $ref_iter ";
+    $cmd .= " $select ";
+    $cmd .= " -inst $camera ";
+    $cmd .= " -select_exp_type $exp_type ";
+    $cmd .= " -select_filter $filter " if defined($filter);
+    $cmd .= " -workdir $workdir ";
+    $cmd .= " -label $label ";
+    if ($maxN > 0) {
+	$cmd .= " -random_subset -random_limit $maxN ";
+    }
+    if ($debug == 1) {
+	$cmd .= ' -pretend ';
+    }
+    print STDERR "$cmd\n";
+    return($cmd);
+}    
+
+sub verify_uniqueness_detverify {
+    my $date = shift;
+    my $target = shift;
+
+    my ($label,$workdir,$filter,$exp_type,$det_type,$ref_det_id,$ref_iter,$maxN) = get_dettool_parameters($date,$target);
+    
+    my $db = init_gpc_db();
+    my $sth = "SELECT * FROM detRun WHERE workdir = '$workdir' AND ref_det_id = $ref_det_id AND ref_iter = $ref_iter";
+    my $data_ref = $db->selectall_arrayref( $sth );
+
+    return($#{ $data_ref } + 1);
+}
+
+sub pre_detrend_queue {
+    my $date = shift;
+    my $target = shift;
+    
+    my $command = construct_dettool_cmd($date,$target) . ' -pretend ';
+    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 dettool: $error_code", 0,0, $date, $PS_EXIT_SYS_ERROR);
+    }
+    
+    my @input_exposures = split /\n/, (join '', @$stdout_buf);
+    return($#input_exposures + 1);
+}
+
+sub detrend_queue {
+    my $date = shift;
+    my $target = shift;
+    
+    my $command = construct_dettool_cmd($date,$target);
+    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 chiptool: $error_code", 0,0,$date, $PS_EXIT_SYS_ERROR);
+    }
+    $metadata_out{nsState} = 'DETREND_QUEUED';
+    return(0);
+
+}
+sub execute_detrends {
+    my $date = shift;
+    my $pretend = shift;
+    my $exposures = 0;
+    foreach my $target (@detrend_list) {
+	my ($Nexposures) = pre_detrend_queue($date,$target);
+	if ($Nexposures == 0) {
+	    print STDERR "execute_detrends: Target $target on $date had no exposures.\n";
+	    next;
+	}
+	$exposures++;
+	unless(defined($pretend)) {
+	    detrend_queue($date,$target);
+	}
+    }
+    if ($exposures == 0) {
+	$metadata_out{nsState} = 'DETREND_DROP';
+    }
+    if (($metadata_out{nsState} eq 'CHECKDETRENDS') && ($exposures > 0)) {
+	$metadata_out{nsState} eq 'QUEUE_DETRENDS';
+    }
+}
+
+#
 # Stacking
 ################################################################################
@@ -627,5 +859,5 @@
     my $cmd = "$stacktool";
 #    $cmd .= ' -pretend -simple -dbname gpc1 -definebyquery -min_new 4 ';  # Probably silly, but I want to be safe and not overwrite
-    $cmd .= ' -simple -dbname gpc1 -definebyquery ';
+    $cmd .= " -simple -dbname $dbname -definebyquery ";
     $cmd .= " -set_label $label -select_label $label ";
     $cmd .= " -set_workdir $workdir -set_dist_group $dist_group ";
@@ -768,5 +1000,14 @@
 
     my $command = $clean_commands{$mode};
-    my $retention_time = $clean_retention{$mode};
+    my $retention_time;
+    if (exists($cleanmods_list{$target}{$mode})) {
+	$retention_time = $cleanmods_list{$target}{$mode};
+    }
+    else {
+	$retention_time = $clean_retention{$mode};
+    }
+    if ($retention_time <= 0) {
+	return("no clean","true");
+    }
 
     my ($year,$month,$day) = split /-/,$date;
@@ -774,4 +1015,5 @@
                                hour => 0, minute => 0, second => 0, nanosecond => 0,
                                time_zone => 'Pacific/Honolulu');
+	
     $dt->subtract(days => $retention_time);
     my $cleaning_date = $dt->ymd;
@@ -779,5 +1021,10 @@
     my ($label,$workdir,$obs_mode,$object,$comment,$tess_id,$dist_group,$data_group,$reduction) = get_tool_parameters($cleaning_date,$target);
     my $args = $command;
-    $args .= " -dbname gpc1 -updaterun -set_state goto_cleaned -state full -set_label goto_cleaned -label $label -data_group $data_group ";
+    if ((exists($clean_alternate{$mode})) && ($clean_alternate{$mode})) {
+	$args .= " -dbname $dbname -updaterun -set_state goto_cleaned -full -set_label goto_cleaned -time_stamp_end $cleaning_date ";
+    }
+    else {
+	$args .= " -dbname $dbname -updaterun -set_state goto_cleaned -state full -set_label goto_cleaned -label $label -data_group $data_group ";
+    }
     if ($debug == 1) {
         $args .= ' -pretend ';
@@ -790,11 +1037,10 @@
     my $pretend = shift;
 
-    foreach my $mode (keys (%clean_commands)) {
-	foreach my $target (@target_list) {
-	    if (exists($noclean_list{$target})) {
+    foreach my $mode (sort (keys (%clean_commands))) {
+	if ((exists($clean_alternate{$mode})) && ($clean_alternate{$mode})) {
+	    my ($cleaning_date,$command) = construct_cleantool_args($date,"",$mode);
+	    if ($cleaning_date eq 'no clean') {
 		next;
 	    }
-	    my ($cleaning_date,$command) = construct_cleantool_args($date,$target,$mode);
-
 	    print STDERR "$command\n";
 	    if (!(defined($pretend) || $debug == 1)) {
@@ -808,4 +1054,25 @@
 	    }
 	}
+	else {
+	    foreach my $target (@target_list) {
+		if (exists($noclean_list{$target})) {
+		    next;
+		}
+		my ($cleaning_date,$command) = construct_cleantool_args($date,$target,$mode);
+		if ($cleaning_date eq 'no clean') {
+		    next;
+		}		
+		print STDERR "$command\n";
+		if (!(defined($pretend) || $debug == 1)) {
+#           print STDERR "BEAR IS DRIVING!?\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 stacktool: $error_code", 0,0,$date, $PS_EXIT_SYS_ERROR);
+		    }
+		}
+	    }
+	}
     }
     return(0);
@@ -826,5 +1093,5 @@
 
     my $label = "${target}.nightlyscience";
-    my $workdir = "neb://\@HOST\@.0/gpc1/${target}.nt/${workdir_date}";
+    my $workdir = "neb://\@HOST\@.0/${dbname}/${target}.nt/${workdir_date}";
     my $obs_mode = $obsmode_list{$target};
     my $object   = $object_list{$target};
@@ -837,4 +1104,30 @@
 }
 
+sub get_dettool_parameters {
+    my $date = shift;
+    my $target = shift;
+    my $workdir_date = $date; $workdir_date =~ s%-%/%g;
+    my $trunc_date = $date; $trunc_date =~ s/-//g;
+
+    my $exp_type = $exptype_list{$target};
+    my $det_type = $dettype_list{$target};
+    my $ref_det_id = $refID_list{$target};
+    my $ref_iter = $refIter_list{$target};
+    my $det_filter = $detfilter_list{$target};
+    my $internal_filter;
+    if (defined($det_filter)) {
+	$internal_filter = $det_filter; $internal_filter =~ s/\..*//;
+	$internal_filter = '.' . $internal_filter;
+    }
+    else {
+	$internal_filter = '';
+    }
+    my $maxN = $detmax_list{$target};    
+    
+    my $lc_type = lc($exp_type);
+    my $label = "${lc_type}${internal_filter}.$trunc_date";
+    my $workdir = 'neb://@HOST@.0/' . $dbname . "/detverify.nt/${workdir_date}/${lc_type}${internal_filter}";
+    return($label,$workdir,$det_filter,$exp_type,$det_type,$ref_det_id,$ref_iter,$maxN);
+}
 
 sub init_gpc_db {
Index: /branches/pap/ippScripts/scripts/diffphot.pl
===================================================================
--- /branches/pap/ippScripts/scripts/diffphot.pl	(revision 28484)
+++ /branches/pap/ippScripts/scripts/diffphot.pl	(revision 28484)
@@ -0,0 +1,300 @@
+#!/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 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 $diffphottool = can_run('diffphottool') or (warn "Can't find diffphottool" and $missing_tools = 1);
+my $psphot = can_run('psphot') or (warn "Can't find psphot" and $missing_tools = 1);
+my $ppArith = can_run('ppArith') or (warn "Can't find ppArith" and $missing_tools = 1);
+my $ppStatsFromMetadata = can_run('ppStatsFromMetadata') or (warn "Can't find ppStatsFromMetadata" and $missing_tools = 1);
+if ($missing_tools) {
+    warn("Can't find required tools.");
+    exit($PS_EXIT_CONFIG_ERROR);
+}
+
+my ($diff_phot_id, $skycell_id, $dbname, $threads, $outroot, $reduction, $verbose, $no_update, $no_op, $redirect, $save_temps);
+GetOptions(
+    'diff_phot_id=s'    => \$diff_phot_id, # Diffphot identifier
+    'skycell_id=s'      => \$skycell_id, # Skycell identifier
+    'dbname|d=s'        => \$dbname, # Database name
+    'threads=s'         => \$threads,   # Number of threads to use
+    'outroot=s'         => \$outroot, # Output root name
+    'reduction=s'       => \$reduction, # Reduction class
+    '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?
+    ) or pod2usage( 2 );
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage(
+    -msg => "Required options: --diff_phot_id --skycell_id --outroot",
+    -exitval => 3,
+    ) unless defined $diff_phot_id
+    and defined $skycell_id
+    and defined $outroot;
+
+my $ipprc = PS::IPP::Config->new() or my_die( "Unable to set up", $diff_phot_id, $skycell_id, $PS_EXIT_CONFIG_ERROR ); # IPP configuration
+
+# XXX camera is not known here; cannot use filerules...
+# my $logDest = $ipprc->filename("LOG.EXP", $outroot);
+my $logDest = "$outroot.log";
+$ipprc->redirect_output($logDest) or my_die( "Unable to redirect output", $diff_phot_id, $skycell_id, $PS_EXIT_SYS_ERROR ) if $redirect;
+
+# Get input components
+my $mdcParser = PS::IPP::Metadata::Config->new; # Parser for metadata config files
+my $inputPath;                                  # Input path
+my $camera;                                     # Camera name
+my $bothways;                           # Diff was done both ways?
+my $magicked;                           # Magic status
+{
+    my $command = "$diffphottool -input -diff_phot_id $diff_phot_id -skycell_id $skycell_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 difftool -inputskyfile: $error_code", $diff_phot_id, $skycell_id, $error_code);
+    }
+
+    my $files = $mdcParser->parse_list(join "", @$stdout_buf) or
+        &my_die("Unable to parse metadata config doc", $diff_phot_id, $skycell_id, $PS_EXIT_PROG_ERROR);
+    &my_die("File list does not contain exactly one entry", $diff_phot_id, $skycell_id, $PS_EXIT_PROG_ERROR) unless scalar @$files == 1;
+    my $file = $$files[0];      # File of interest
+    $inputPath = $file->{path_base};
+    $camera = $file->{camera};
+    $bothways = $file->{bothways};
+    $magicked = $file->{magicked};
+}
+
+&my_die("Unable to identify input", $diff_phot_id, $skycell_id, $PS_EXIT_SYS_ERROR) unless defined $inputPath;
+&my_die("Unable to identify camera", $diff_phot_id, $skycell_id, $PS_EXIT_SYS_ERROR) unless defined $camera;
+$ipprc->define_camera($camera);
+
+# Recipes to use based on reduction class
+$reduction = 'DEFAULT' unless defined $reduction;
+my $recipe_psphot  = $ipprc->reduction($reduction, 'DIFF_PSPHOT'); # Recipe to use for psphot
+my $recipe_ppstats = 'DIFFSTATS';
+&my_die("Couldn't find selected reduction for DIFF_PSPHOT: $reduction\n", $diff_phot_id, $skycell_id, $PS_EXIT_CONFIG_ERROR) unless ($recipe_psphot);
+
+# Input filenames
+my $inputImage = $ipprc->filename("PPSUB.OUTPUT", $inputPath);
+my $inputMask = $ipprc->filename("PPSUB.OUTPUT.MASK", $inputPath);
+my $inputVariance = $ipprc->filename("PPSUB.OUTPUT.VARIANCE", $inputPath);
+my $inputPSF = $ipprc->filename("PSPHOT.PSF.SKY.SAVE", $inputPath);
+&my_die("Couldn't find input: $inputImage", $diff_phot_id, $skycell_id, $PS_EXIT_SYS_ERROR) unless $ipprc->file_exists($inputImage);
+&my_die("Couldn't find input: $inputMask", $diff_phot_id, $skycell_id, $PS_EXIT_SYS_ERROR) unless $ipprc->file_exists($inputMask);
+&my_die("Couldn't find input: $inputVariance", $diff_phot_id, $skycell_id, $PS_EXIT_SYS_ERROR) unless $ipprc->file_exists($inputVariance);
+&my_die("Couldn't find input: $inputPSF", $diff_phot_id, $skycell_id, $PS_EXIT_SYS_ERROR) unless $ipprc->file_exists($inputPSF);
+
+my $quality;                    # Quality flag
+
+# Do forward photometry
+{
+    # Get the output filenames
+    my $outputName = $ipprc->filename("PSPHOT.OUT.CMF.MEF", "$outroot.pos");
+    my $outputStats = $ipprc->filename("SKYCELL.STATS", "$outroot.pos");
+    my $traceDest = $ipprc->filename("TRACE.EXP", "$outroot.pos");
+
+    my $command = "$psphot $outroot.pos";
+    $command .= " -file $inputImage";
+    $command .= " -mask $inputMask";
+    $command .= " -variance $inputVariance";
+    $command .= " -psf $inputPSF";
+#    $command .= " -stats $outputStats";
+    $command .= " -threads $threads" if defined $threads;
+    $command .= " -recipe PSPHOT $recipe_psphot";
+    $command .= " -recipe PPSTATS $recipe_ppstats";
+    $command .= " -F PSPHOT.PSF.SAVE PSPHOT.PSF.SKY.SAVE";
+    $command .= " -F PSPHOT.OUTPUT PSPHOT.OUT.CMF.MEF";
+    $command .= " -F PSPHOT.BACKMDL PSPHOT.BACKMDL.MEF";
+    $command .= " -tracedest $traceDest -log $logDest";
+    $command .= " -dbname $dbname" if defined $dbname;
+
+    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 psphot: $error_code", $diff_phot_id, $skycell_id, $error_code);
+        }
+
+        if (0) {                # psphot doesn't produce a stats file
+        my $outputStatsReal = $ipprc->file_resolve($outputStats);
+        &my_die("Couldn't find expected output file: $outputStats", $diff_phot_id, $skycell_id, $PS_EXIT_SYS_ERROR) unless $ipprc->file_exists($outputStatsReal);
+
+        $command = "$ppStatsFromMetadata $outputStatsReal - DIFF_SKYCELL";
+        ( $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 ppStatsFromMetadata: $error_code", $diff_phot_id, $skycell_id, $error_code);
+        }
+        my $cmdflags = "";
+        foreach my $line (@$stdout_buf) {
+            $cmdflags .= " $line";
+        }
+        chomp $cmdflags;
+        if ($cmdflags =~ /-quality (\d+)/) {
+            $quality = $1;
+        }
+        }
+
+        if (!$quality) {
+            &my_die("Couldn't find expected output file: $outputName", $diff_phot_id, $skycell_id, $PS_EXIT_SYS_ERROR) unless $ipprc->file_exists($outputName);
+        }
+    } else {
+        print "Not executing: $command\n";
+    }
+}
+
+# Do reverse photometry
+if ($bothways) {
+    my ($tempFile, $tempName) = tempfile( "/tmp/diffphot.$diff_phot_id.$skycell_id.XXXX",
+                                          UNLINK => !$save_temps, SUFFIX => '.fits' );
+    {
+        my ($tempRoot) = $tempName =~ /^(.*).fits/;
+        my $command = "$ppArith $tempRoot -file1 $inputImage -op \\* -const2 -1";
+        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 ppArith: $error_code", $diff_phot_id, $skycell_id, $error_code);
+            }
+            &my_die("Couldn't find expected output file: $tempName", $diff_phot_id, $skycell_id, $PS_EXIT_SYS_ERROR) unless $ipprc->file_exists($tempName);
+        } else {
+            print "Skipping command: $command\n";
+        }
+    }
+
+    # Get the output filenames
+    my $outputName = $ipprc->filename("PSPHOT.OUT.CMF.MEF", "$outroot.neg");
+    my $outputStats = $ipprc->filename("SKYCELL.STATS", "$outroot.neg");
+    my $traceDest = $ipprc->filename("TRACE.EXP", "$outroot.neg");
+
+    my $command = "$psphot $outroot.neg";
+    $command .= " -file $tempName";
+    $command .= " -mask $inputMask";
+    $command .= " -variance $inputVariance";
+    $command .= " -psf $inputPSF";
+#    $command .= " -stats $outputStats";
+    $command .= " -threads $threads" if defined $threads;
+    $command .= " -recipe PSPHOT $recipe_psphot";
+    $command .= " -recipe PPSTATS $recipe_ppstats";
+    $command .= " -F PSPHOT.PSF.SAVE PSPHOT.PSF.SKY.SAVE";
+    $command .= " -F PSPHOT.OUTPUT PSPHOT.OUT.CMF.MEF";
+    $command .= " -F PSPHOT.BACKMDL PSPHOT.BACKMDL.MEF";
+    $command .= " -tracedest $traceDest -log $logDest";
+    $command .= " -dbname $dbname" if defined $dbname;
+
+    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 psphot: $error_code", $diff_phot_id, $skycell_id, $error_code);
+        }
+
+        if (0) {                # psphot doesn't produce a stats file
+        my $outputStatsReal = $ipprc->file_resolve($outputStats);
+        &my_die("Couldn't find expected output file: $outputStats", $diff_phot_id, $skycell_id, $PS_EXIT_SYS_ERROR) unless $ipprc->file_exists($outputStatsReal);
+
+        $command = "$ppStatsFromMetadata $outputStatsReal - DIFF_SKYCELL";
+        ( $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 ppStatsFromMetadata: $error_code", $diff_phot_id, $skycell_id, $error_code);
+        }
+        my $cmdflags = "";
+        foreach my $line (@$stdout_buf) {
+            $cmdflags .= " $line";
+        }
+        chomp $cmdflags;
+        if ($cmdflags =~ /-quality (\d+)/) {
+            $quality = $1;
+        }
+        }
+
+        if (!$quality) {
+            &my_die("Couldn't find expected output file: $outputName", $diff_phot_id, $skycell_id, $PS_EXIT_SYS_ERROR) unless $ipprc->file_exists($outputName);
+        }
+    } else {
+        print "Not executing: $command\n";
+    }
+}
+
+unless ($no_update) {
+    my $command = "$diffphottool -diff_phot_id $diff_phot_id -skycell_id $skycell_id";
+    $command .= " -done -path_base $outroot";
+    $command .= " -magicked $magicked" if $magicked;
+    $command .= " -quality $quality" if defined $quality;
+    $command .= (" -dtime_script " . ((DateTime->now->mjd - $mjd_start) * 86400));
+    $command .= " -hostname $host" if defined $host;
+    $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("Command failed: $error_code", $diff_phot_id, $skycell_id, $error_code);
+    }
+}
+
+
+sub my_die
+{
+    my $msg = shift;            # Warning message on die
+    my $diff_phot_id = shift;        # Diff identifier
+    my $skycell_id = shift;     # Skycell identifier
+    my $exit_code = shift;      # Exit code to add
+
+    $exit_code = $PS_EXIT_PROG_ERROR unless defined $exit_code;
+
+    warn($msg);
+    if (defined $diff_phot_id and defined $skycell_id and not $no_update) {
+        my $command = "$diffphottool -done";
+        $command .= " -diff_phot_id $diff_phot_id -skycell_id $skycell_id -fault $exit_code";
+        $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/pap/ippScripts/scripts/minidvodb_createdb.pl
===================================================================
--- /branches/pap/ippScripts/scripts/minidvodb_createdb.pl	(revision 28484)
+++ /branches/pap/ippScripts/scripts/minidvodb_createdb.pl	(revision 28484)
@@ -0,0 +1,266 @@
+#!/usr/bin/env perl
+
+use warnings;
+use strict;
+use Carp;
+use DateTime;
+
+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 $addtool = can_run('addtool') or (warn "Can't find addtool" 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, $dvodb, $minidvodb,$minidvodb_interval, $minidvodb_group, $camera,  $verbose, $no_update,
+     $no_op, $redirect, $save_temps);
+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
+    'dvodb|w=s'         => \$dvodb,  # output DVO database
+    'minidvodb|w=s'     => \$minidvodb, # output miniDVODB
+    'interval|w=s'      => \$minidvodb_interval, #interval between creation of minidvodbs (default = 1day)
+    '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: --camera  --dvodb --minidvodb_group  --outroot",
+          -exitval => 3,
+          ) unless
+    defined $minidvodb_group and
+    defined $camera and
+    defined $outroot and
+    defined $dvodb;
+
+my $ipprc = PS::IPP::Config->new( $camera ) or my_die( "Unable to set up", $minidvodb_group   , $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);
+
+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
+
+# XXX This is now not used: do we still need it?
+
+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 $dvodbReal;
+if (defined $dvodb) {
+    $dvodbReal = $ipprc->dvo_catdir( $dvodb ); # catdir for DVO
+    $dvodbReal = $ipprc->convert_filename_absolute( $dvodbReal );
+} else {
+    warn("dvodb undefined:\n");
+    exit(4);
+}
+my $minidvodbReal;
+if (defined $minidvodb) {
+    $minidvodbReal = $ipprc->dvo_catdir( $minidvodb ); # catdir for DVO
+    $minidvodbReal = $ipprc->convert_filename_absolute( $minidvodbReal );
+} else {
+    warn("minidvodb undefined:\n");
+    exit(4);
+}
+
+
+if (!defined $minidvodb_interval) {
+    $minidvodb_interval = 1;
+}
+
+
+unless ($no_op) {
+
+
+
+#see if there is already one in new state
+    my $fpaCommand1 = "$addtool -listminidvodbrun";
+    $fpaCommand1 .= " -minidvodb_group '$minidvodb_group'";
+    $fpaCommand1 .= " -state 'new'";
+    $fpaCommand1 .= " -dbname $dbname" if defined $dbname;
+
+
+unless ($no_update) {
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $fpaCommand1, verbose => $verbose);
+
+
+
+   unless ($success) {
+       $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+       warn("Unable to list minidvodb database: $error_code\n");
+       exit($error_code);
+    }
+
+    if (scalar(@{$stdout_buf})) {
+        $error_code = 3;
+            warn("Unwilling to create minidvodb, one already exists in new state: $error_code\n");
+        exit($error_code);
+    }
+
+    my $creation_date;
+    my $addRun_count;
+    my $minidvodb_name;
+#find the active one's date, and find out if it has more than 1 addRun in it
+
+    my $fpaCommand2 = "$addtool -listminidvodbrun";
+    $fpaCommand2 .= " -minidvodb_group '$minidvodb_group'";
+    $fpaCommand2 .= " -state 'active'";
+    $fpaCommand2 .= " -limit 1";
+     $fpaCommand2 .= " -dbname $dbname" if defined $dbname;
+
+#print $fpaCommand2;
+
+
+    my ( $success2, $error_code2, $full_buf2, $stdout_buf2, $stderr_buf2 ) =
+        run(command => $fpaCommand2, verbose => $verbose);
+    &my_die( "Unable to get listminidvodbrun",$minidvodb_group, $PS_EXIT_SYS_ERROR) unless $success2;
+  # if it didn't list something in active state (what?) then we definitely need to create a new one
+    if (defined(@$stdout_buf2)) {
+  my  $metadata2 = $mdcParser->parse(join "", @$stdout_buf2) or
+        &my_die("Unable to parse metadata config", $minidvodb_group, $PS_EXIT_PROG_ERROR);
+
+  my   $components2 = parse_md_list($metadata2) or
+        &my_die("Unable to parse metadata list", $minidvodb_group, $PS_EXIT_PROG_ERROR);
+  my   $comp2 = $$components2[0];
+    $minidvodb_name = $comp2->{minidvodb_name};
+    $creation_date  = $comp2->{creation_date};
+    if (!defined($minidvodb_name)) {
+        &my_die("Unable to parse minidvodb_name", $minidvodb_group, $PS_EXIT_PROG_ERROR);
+    }
+    if (!defined($creation_date)) {
+        &my_die("Unable to parse creation_date", $minidvodb_group, $PS_EXIT_PROG_ERROR);
+    }
+    } else {
+        $create_new = 1; #this is to force it to make a new one
+            }
+
+    #find the number of add_ids that have been proccessed
+    my $fpaCommand3 = "$addtool -checkminidvodbrunaddrun";
+    $fpaCommand3 .= " -minidvodb_group '$minidvodb_group'";
+    $fpaCommand3 .= " -state 'active'";
+    $fpaCommand3 .= " -minidvodb_name '$minidvodb_name'" if defined $minidvodb_name;
+    $fpaCommand3 .= " -limit 1";
+    $fpaCommand3 .= " -dbname $dbname" if defined $dbname;
+
+
+my ( $success3, $error_code3, $full_buf3, $stdout_buf3, $stderr_buf3 ) =
+        run(command => $fpaCommand3, verbose => $verbose);
+    &my_die( "Unable to get checkminidvodbunaddrun", $minidvodb_group, $PS_EXIT_SYS_ERROR) unless $success3;
+
+    if (defined(@$stdout_buf3)) {  #checkminidvodb returns nothing IF there have been no addruns added to the db yet
+    my  $metadata3 = $mdcParser->parse(join "", @$stdout_buf3) or
+        &my_die("Unable to parse metadata config", $minidvodb_group, $PS_EXIT_PROG_ERROR);
+
+    my  $components3 = parse_md_list($metadata3) or
+        &my_die("Unable to parse metadata list", $minidvodb_group, $PS_EXIT_PROG_ERROR);
+   my  $comp = $$components3[0];
+    $addRun_count = $comp->{addRun_count};
+        }
+    if (!defined($addRun_count)) {
+         ## there's nothing to parse if there's nothing
+        $addRun_count = 0;
+    }
+
+
+    if ($addRun_count > 500) {
+        #it's too big, create_new
+        $create_new = 1;
+   }
+    if ($create_new == 0) {
+        my $parser = DateTime::Format::Strptime->new( pattern => '%Y-%m-%dT%H:%M:%S', time_zone => "HST" );
+        my $creation_dt = $parser->parse_datetime( $creation_date )->mjd;
+        if ($mjd_start- $creation_dt > $minidvodb_interval && $addRun_count > 0 ) {
+            #db is old and has stuff in it, want to create_new
+            $create_new = 1;
+        }
+    }
+
+
+}
+#create the minidvodb entry (well, the command for it)
+    my $fpaCommand = "$addtool -addminidvodbrun";
+    $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;
+
+    unless ($no_update or !$create_new) {
+
+
+        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 $minidvodb_group = 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/pap/ippScripts/scripts/minidvodb_merge.pl
===================================================================
--- /branches/pap/ippScripts/scripts/minidvodb_merge.pl	(revision 28484)
+++ /branches/pap/ippScripts/scripts/minidvodb_merge.pl	(revision 28484)
@@ -0,0 +1,254 @@
+#!/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_merge;
+
+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);
+if ($missing_tools) {
+    warn("Can't find required tools.");
+    exit($PS_EXIT_CONFIG_ERROR);
+}
+
+my ( $mergedvodb, $minidvodb, $minidvodb_id, $minidvodb_group, $camera, $dbname,$verbose, $logfile, $no_op, $redirect, $save_temps);
+GetOptions(
+    'mergedvodb|w=s'         => \$mergedvodb,  # output DVO database
+    '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: --mergedvodb --minidvodb --minidvodb_id -- minidvodb_group --camera",
+          -exitval => 3,
+          ) unless
+    defined $mergedvodb and
+    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", $mergedvodb, $PS_EXIT_CONFIG_ERROR ); # IPP configuration
+
+if ($logfile) {
+    $ipprc->redirect_output($logfile) or my_die( "Unable to redirect output", $mergedvodb, $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
+my $mergedvodbReal;
+if (defined $mergedvodb) {
+    $mergedvodbReal = $ipprc->dvo_catdir( $mergedvodb ); # catdir for DVO
+    $mergedvodbReal = $ipprc->convert_filename_absolute( $mergedvodbReal );
+}
+
+my $dtime_addstar = 0;
+#my $dtime_relphot = 0;
+
+unless ($no_op) {
+    if (defined $mergedvodbReal) {
+        {
+            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", $mergedvodb, $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 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", $mergedvodb, $error_code);
+            }
+            $dtime_relphot = 86400.0*(DateTime->now->mjd - $mjd_relphot_start);   # MJD of starting script
+            print "relphot time $dtime_relphot\n";
+        }
+
+        my $this_is_the_first;
+        {
+
+            my $mdcParser = PS::IPP::Metadata::Config->new;
+
+            my $command = "$addtool -listminidvodbrun -state merged -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 minidvodbruns", $minidvodb_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", $minidvodb_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", $minidvodb_id, $PS_EXIT_PROG_ERROR);
+                my $comp = $$components[0];
+                my  $minidvodb_name = $comp->{minidvodb_name};
+
+                if (!defined($minidvodb_name)) {
+                    &my_die("Unable to parse minidvodb_name", $minidvodb_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 minidvodbrun in merged state\n";
+                $this_is_the_first = 0;
+            }
+        }
+	print "$this_is_the_first $mergedvodb/Image.dat\n";
+	
+	 
+	if (-e "$mergedvodb/Image.dat") {
+	    if ($this_is_the_first == 1) {
+		&my_die("refusing to merge, this is the first, but files already exist in dir", $minidvodb_id, 4);
+	    }
+	    $this_is_the_first =0;
+	}
+	
+	print "$this_is_the_first $mergedvodb/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/* $mergedvodb";
+	    } else {
+                $merge_command = "$dvomerge $minidvodb into $mergedvodb";
+	    }
+	    
+	    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", $mergedvodb, $error_code);
+	    }
+		
+	    $dtime_merge = 86400.0*(DateTime->now->mjd - $mjd_merge_start);   # MJD of starting script
+	    print "merge time $dtime_merge\n";
+#	}
+	}
+
+	{
+            my $command = "addtool -minidvodb_id $minidvodb_id";
+            $command .= " -addminidvodbprocessed";
+            $command .= " -mergedvodb_path $mergedvodbReal" if defined $mergedvodbReal;
+            $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_merge $dtime_merge" if defined $dtime_merge;
+            $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", $mergedvodb, $error_code);
+            }
+        }
+
+    } else {
+        &my_die("dvodb: $mergedvodb not found", $mergedvodb, $PS_EXIT_UNKNOWN_ERROR);
+    }
+} else {
+    print "skipping processing for $mergedvodbReal\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 $mergedvodb\n";
+
+if (defined $minidvodb_id ) {
+
+    my $command = "addtool -minidvodb_id $minidvodb_id";
+    $command .= " -addminidvodbprocessed";
+        $command .= " -fault $exit_code";
+        $command .= " -mergedvodb_path $mergedvodbReal" if defined $mergedvodbReal;
+        $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_merge $dtime_merge" if defined $dtime_merge;
+        $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/pap/ippScripts/scripts/publish_file.pl
===================================================================
--- /branches/pap/ippScripts/scripts/publish_file.pl	(revision 28483)
+++ /branches/pap/ippScripts/scripts/publish_file.pl	(revision 28484)
@@ -140,6 +140,11 @@
     print $dsFile "$file|||$product|$name|\n";
 
-} elsif ($stage eq 'diff') {
-    my $command =  "difftool -diffskyfile -diff_id $stage_id";
+} elsif ($stage eq 'diff' or $stage eq 'diffphot') {
+    my $command;                # Command to run
+    if ($stage eq 'diff') {
+        $command = "difftool -diffskyfile -diff_id $stage_id";
+    } elsif ($stage eq 'diffphot') {
+        $command = "diffphottool -data -diff_phot_id $stage_id";
+    }
     $command .= " -dbname $dbname" if defined $dbname;
     my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
@@ -147,12 +152,10 @@
     &my_die( "Unable to retrieve filename", $pub_id, $PS_EXIT_SYS_ERROR) unless $success;
 
-    my $metadata = $mdcParser->parse(join "", @$stdout_buf) or
+    my $components = $mdcParser->parse_list(join "", @$stdout_buf) or
         &my_die("Unable to parse metadata config", $pub_id, $PS_EXIT_PROG_ERROR);
 
-    my $components = parse_md_list($metadata) or
-        &my_die("Unable to parse metadata list", $pub_id, $PS_EXIT_PROG_ERROR);
-
-    my ($mopsPositiveFile, $mopsPositiveFileName) = tempfile("/tmp/publish.$pub_id.mops.pos.XXXX", UNLINK => !$save_temps ) if $product eq 'IPP-MOPS';
-    my ($mopsNegativeFile, $mopsNegativeFileName) = tempfile("/tmp/publish.$pub_id.mops.neg.XXXX", UNLINK => !$save_temps ) if $product eq 'IPP-MOPS';
+    my $mops = ($product =~ /^IPP-MOPS/ ? 1 : 0); # Format for MOPS?
+    my ($mopsPositiveFile, $mopsPositiveFileName) = tempfile("/tmp/publish.$pub_id.mops.pos.XXXX", UNLINK => !$save_temps ) if $mops;
+    my ($mopsNegativeFile, $mopsNegativeFileName) = tempfile("/tmp/publish.$pub_id.mops.neg.XXXX", UNLINK => !$save_temps ) if $mops;
 
     my %positive;               # Data for positive diff detections
@@ -169,5 +172,10 @@
 
         my $skycell_id = $comp->{skycell_id};
-        my $filename = $ipprc->filename( "PPSUB.OUTPUT.SOURCES", $path_base );
+        my $filename;
+        if ($stage eq 'diff') {
+            $filename = $ipprc->filename( "PPSUB.OUTPUT.SOURCES", $path_base );
+        } elsif ($stage eq 'diffphot') {
+            $filename = $ipprc->filename( "PSPHOT.OUT.CMF.MEF", "$path_base.pos" );
+        }
         $filename = $ipprc->file_resolve($filename);
 
@@ -187,5 +195,5 @@
         diff_check(\%positive, $data, "positive");
 
-        if ($product eq 'IPP-MOPS') {
+        if ($mops) {
             print $mopsPositiveFile "$filename\n";
         } else {
@@ -195,5 +203,11 @@
         # Negative direction
         if (defined $comp->{bothways} and $comp->{bothways}) {
-            my $filename = $ipprc->filename( "PPSUB.INVERSE.SOURCES", $path_base );
+            my $filename;
+            if ($stage eq 'diff') {
+                $filename = $ipprc->filename( "PPSUB.INVERSE.SOURCES", $path_base );
+            } elsif ($stage eq 'diffphot') {
+                $filename = $ipprc->filename( "PSPHOT.OUT.CMF.MEF", "$path_base.neg" );
+            }
+
             $filename = $ipprc->file_resolve($filename);
 
@@ -213,5 +227,5 @@
             diff_check(\%negative, $data, "negative");
 
-            if ($product eq 'IPP-MOPS') {
+            if ($mops) {
                 print $mopsNegativeFile "$filename\n";
             } else {
@@ -221,8 +235,8 @@
     }
 
-    close $mopsPositiveFile if $product eq 'IPP-MOPS';
-    close $mopsNegativeFile if $product eq 'IPP-MOPS';
-
-    if ($product eq 'IPP-MOPS') {
+    close $mopsPositiveFile if $mops;
+    close $mopsNegativeFile if $mops;
+
+    if ($mops) {
         if (scalar keys %positive > 0) {
             my $output = mops_combine(\%positive, "$outroot.pos.mops", $mopsPositiveFileName);
@@ -239,5 +253,5 @@
 
 unless ($no_update) {
-    my $command = "$dsreg --add $stage.$stage_id --copy --abspath --product $product --type $product --list $dsFileName";
+    my $command = "$dsreg --add pub.$pub_id.$stage.$stage_id --copy --abspath --product $product --type $product --list $dsFileName";
     $command .= " --ps0 \"$comment\"" if defined $comment;
 
Index: /branches/pap/ippScripts/scripts/skycell_jpeg.pl
===================================================================
--- /branches/pap/ippScripts/scripts/skycell_jpeg.pl	(revision 28484)
+++ /branches/pap/ippScripts/scripts/skycell_jpeg.pl	(revision 28484)
@@ -0,0 +1,360 @@
+#! /usr/bin/env perl
+
+use warnings;
+use strict;
+use Carp;
+use IPC::Cmd 0.36 qw( can_run run);
+use PS::IPP::Metadata::List qw( parse_md_list );
+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::Temp qw( tempfile );
+
+my $db;
+
+my ($stage, $stage_id, $outroot, $camera, $dbname, $logfile, $verbose, $save_temps);
+my $missing_tools;
+my ($help,$masks);
+my ($tempFile,$tempName,$maskFile,$maskName);
+my $ppSkycell = can_run('ppSkycell') or (warn "Can't find ppSkycell" and $missing_tools = 1);
+my $warptool  = can_run('warptool') or (warn "Can't find warptool" and $missing_tools = 1);
+my $stacktool = can_run('stacktool') or (warn "Can't find stacktool" and $missing_tools = 1);
+my $difftool  = can_run('difftool') or (warn "Can't find difftool" and $missing_tools = 1);
+GetOptions(
+    'help|h'          => \$help,
+    'stage=s'         => \$stage,
+    'stage_id=s'      => \$stage_id,
+    'outroot=s'       => \$outroot,
+    'dbname=s'        => \$dbname,
+    'camera=s'        => \$camera,
+    'logfile=s'       => \$logfile,
+    'verbose'         => \$verbose,
+    'save_temps'      => \$save_temps,
+    'masks'           => \$masks,
+#    'no-op'           => \$no_op,
+#    'no-update'       => \$no_update,
+    ) or pod2usage ( 2 );
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2) if @ARGV;
+pod2usage(
+    -msg => "Required options: --stage --stage_id --path_base\nHelpful options: --camera --dbname",
+    -exitval => 3,
+    ) unless
+    defined $stage and defined $stage_id and defined $outroot;
+
+my $ipprc = PS::IPP::Config->new( $camera ) or my_die("Unable to set up", $stage_id, $PS_EXIT_CONFIG_ERROR); # this is used for PATH, NEB filename conversions
+
+my %stages = ('warp' => 1, 'stack' => 1, 'diff' => 1);
+
+unless (exists($stages{$stage})) {
+    my_die("Unknown stage '$stage'",$stage_id,$PS_EXIT_UNKNOWN_ERROR);
+}
+
+my $mdcParser = PS::IPP::Metadata::Config->new; # Parser for metadata config files
+
+if ($stage eq 'warp') {
+    my $imfiles;
+    my $command = "$warptool -warped -warp_id $stage_id";
+    if (defined($dbname)) {
+	$command .= " -dbname $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 warptool: $error_code", $stage_id, $error_code);
+    }
+
+    if (@$stdout_buf == 0) {
+	# no results found;
+    }
+    
+    $imfiles = $mdcParser->parse_list(join "", @$stdout_buf) or
+	&my_die("unable to parse metadata config doc", $stage_id, $error_code);
+
+    my %tangents = ();
+    
+    foreach my $imfile (@$imfiles) {
+	my $skycell_id = $imfile->{skycell_id};
+	my $path_base  = $imfile->{path_base};
+	my $quality    = $imfile->{quality};
+	my $state      = $imfile->{data_state};
+	my $projection_cell = $skycell_id;
+	if ($quality != 0 or $state ne 'full') {
+	    next;
+	}
+
+	$projection_cell =~ s/^(.*)\..*$/$1/;
+	
+	unless (exists($tangents{$projection_cell})) {
+	    # Make a temp file and fill, but be sure to save 
+	    ($tempFile, $tempName) = tempfile("/tmp/skycell.$projection_cell.XXXX",
+						 UNLINK => !$save_temps);
+	    $tangents{$projection_cell}{FILE} = $tempFile;
+	    $tangents{$projection_cell}{NAME} = $tempName;
+	    if ($masks) {
+		my ($maskFile, $maskName) = tempfile("/tmp/skycell.$projection_cell.masks.XXXX",
+						     UNLINK => !$save_temps);
+		$tangents{$projection_cell}{MFILE} = $maskFile;
+		$tangents{$projection_cell}{MNAME} = $maskName;
+	    }		
+	}
+	print "$skycell_id $projection_cell\n";	
+	my $file = $ipprc->filename("PSWARP.OUTPUT", $path_base, $skycell_id);
+	print "$file $state $quality\n";
+	my $f_fh = $tangents{$projection_cell}{FILE};
+	print $f_fh "$file\n";
+	if ($masks) {
+	    my $mask = $ipprc->filename("PSWARP.OUTPUT.MASK", $path_base, $skycell_id);
+	    print "$mask\n";
+	    my $m_fh = $tangents{$projection_cell}{MFILE};
+	    print $m_fh "$mask\n";
+	}
+    }
+    foreach my $projection_cell (keys %tangents) {
+	$command = "$ppSkycell -images $tangents{$projection_cell}{NAME}";
+	if ($masks) {
+	    $command .= " -masks $tangents{$projection_cell}{MNAME} ";
+	}
+	$command .= " ${outroot}.${projection_cell} ";
+	print "$command\n";
+	( $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 ppSkycell: $error_code", $stage_id, $error_code);
+	}
+	# Update database:
+	$command = "$warptool -addsummary -warp_id $stage_id -projection_cell $projection_cell -path_base $outroot";
+	if (defined($dbname)) {
+	    $command .= " -dbname $dbname";
+	}
+	
+	( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) = run(command => $command, verbose => $verbose);
+	unless ($success) {
+	    $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+	    &my_die("unable to update warp summary: $error_code", $stage_id, $error_code);
+	}
+    }
+    if (scalar (keys %tangents) == 0) {
+	my $projection_cell = 'NULL';
+	$command = "$warptool -addsummary -warp_id $stage_id -projection_cell $projection_cell -path_base $outroot";
+	if (defined($dbname)) {
+	    $command .= " -dbname $dbname";
+	}
+	
+	( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) = run(command => $command, verbose => $verbose);
+	unless ($success) {
+	    $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+	    &my_die("unable to update diff summary: $error_code", $stage_id, $error_code);
+	}
+    }	
+} #end warp stage
+if ($stage eq 'diff') {
+    my $imfiles;
+    my $command = "$difftool -diffskyfile -diff_id $stage_id";
+    if (defined($dbname)) {
+	$command .= " -dbname $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 difftool: $error_code", $stage_id, $error_code);
+    }
+
+    if (@$stdout_buf == 0) {
+	# no results found;
+    }
+    
+    $imfiles = $mdcParser->parse_list(join "", @$stdout_buf) or
+	&my_die("unable to parse metadata config doc", $stage_id, $error_code);
+
+    my %tangents = ();
+    
+    foreach my $imfile (@$imfiles) {
+	my $skycell_id = $imfile->{skycell_id};
+	my $path_base  = $imfile->{path_base};
+	my $quality    = $imfile->{quality};
+	my $state      = $imfile->{data_state};
+	my $projection_cell = $skycell_id;
+	if ($quality != 0 or $state ne 'full') {
+	    next;
+	}
+
+	$projection_cell =~ s/^(.*)\..*$/$1/;
+	
+	unless (exists($tangents{$projection_cell})) {
+	    # Make a temp file and fill, but be sure to save 
+	    ($tempFile, $tempName) = tempfile("/tmp/skycell.$projection_cell.XXXX",
+						 UNLINK => !$save_temps);
+	    $tangents{$projection_cell}{FILE} = $tempFile;
+	    $tangents{$projection_cell}{NAME} = $tempName;
+	    if ($masks) {
+		my ($maskFile, $maskName) = tempfile("/tmp/skycell.$projection_cell.masks.XXXX",
+						     UNLINK => !$save_temps);
+		$tangents{$projection_cell}{MFILE} = $maskFile;
+		$tangents{$projection_cell}{MNAME} = $maskName;
+	    }		
+	}
+	print "$skycell_id $projection_cell\n";	
+	my $file = $ipprc->filename("PPSUB.OUTPUT", $path_base, $skycell_id);
+	print "$file $state $quality\n";
+	my $f_fh = $tangents{$projection_cell}{FILE};
+	print $f_fh "$file\n";
+	if ($masks) {
+	    my $mask = $ipprc->filename("PPSUB.OUTPUT.MASK", $path_base, $skycell_id);
+	    print "$mask\n";
+	    my $m_fh = $tangents{$projection_cell}{MFILE};
+	    print $m_fh "$mask\n";
+	}
+    }
+    foreach my $projection_cell (keys %tangents) {
+	$command = "$ppSkycell -images $tangents{$projection_cell}{NAME}";
+	if ($masks) {
+	    $command .= " -masks $tangents{$projection_cell}{MNAME} ";
+	}
+	$command .= " ${outroot}.${projection_cell} ";
+	print "$command\n";
+	( $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 ppSkycell: $error_code", $stage_id, $error_code);
+	}
+	# Update database:
+	$command = "$difftool -addsummary -diff_id $stage_id -projection_cell $projection_cell -path_base $outroot";
+	if (defined($dbname)) {
+	    $command .= " -dbname $dbname";
+	}
+	
+	( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) = run(command => $command, verbose => $verbose);
+	unless ($success) {
+	    $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+	    &my_die("unable to update diff summary: $error_code", $stage_id, $error_code);
+	}
+    }
+    if (scalar (keys %tangents) == 0) {
+	my $projection_cell = 'NULL';
+	$command = "$difftool -addsummary -diff_id $stage_id -projection_cell $projection_cell -path_base $outroot";
+	if (defined($dbname)) {
+	    $command .= " -dbname $dbname";
+	}
+	
+	( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) = run(command => $command, verbose => $verbose);
+	unless ($success) {
+	    $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+	    &my_die("unable to update diff summary: $error_code", $stage_id, $error_code);
+	}
+    }	
+} #end diff stage
+if ($stage eq 'stack') {
+    my $imfiles;
+    my $command = "$stacktool -sassskyfile -sass_id $stage_id";
+    if (defined($dbname)) {
+	$command .= " -dbname $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 stacktool: $error_code", $stage_id, $error_code);
+    }
+
+    if (@$stdout_buf == 0) {
+	# no results found;
+    }
+    
+    $imfiles = $mdcParser->parse_list(join "", @$stdout_buf) or
+	&my_die("unable to parse metadata config doc", $stage_id, $error_code);
+
+    my %tangents = ();
+    
+    foreach my $imfile (@$imfiles) {
+	my $skycell_id = $imfile->{skycell_id};
+	my $path_base  = $imfile->{path_base};
+	my $quality    = $imfile->{quality};
+	my $state      = $imfile->{state};
+	my $fault      = $imfile->{fault};
+	my $projection_cell = $skycell_id;
+	if ($quality != 0 or $state ne 'full') {
+	    next;
+	}
+
+	$projection_cell =~ s/^(.*)\..*$/$1/;
+	
+	unless (exists($tangents{$projection_cell})) {
+	    # Make a temp file and fill, but be sure to save 
+	    ($tempFile, $tempName) = tempfile("/tmp/skycell.$projection_cell.XXXX",
+						 UNLINK => !$save_temps);
+	    $tangents{$projection_cell}{FILE} = $tempFile;
+	    $tangents{$projection_cell}{NAME} = $tempName;
+	    if ($masks) {
+		my ($maskFile, $maskName) = tempfile("/tmp/skycell.$projection_cell.masks.XXXX",
+						     UNLINK => !$save_temps);
+		$tangents{$projection_cell}{MFILE} = $maskFile;
+		$tangents{$projection_cell}{MNAME} = $maskName;
+	    }		
+	}
+	print "$skycell_id $projection_cell\n";	
+	my $file = $ipprc->filename("PPSTACK.OUTPUT", $path_base, $skycell_id);
+	print "$file $state $quality\n";
+	my $f_fh = $tangents{$projection_cell}{FILE};
+	print $f_fh "$file\n";
+	if ($masks) {
+	    my $mask = $ipprc->filename("PPSTACK.OUTPUT.MASK", $path_base, $skycell_id);
+	    print "$mask\n";
+	    my $m_fh = $tangents{$projection_cell}{MFILE};
+	    print $m_fh "$mask\n";
+	}
+    }
+    foreach my $projection_cell (keys %tangents) {
+	$command = "$ppSkycell -images $tangents{$projection_cell}{NAME}";
+	if ($masks) {
+	    $command .= " -masks $tangents{$projection_cell}{MNAME} ";
+	}
+	$command .= " ${outroot}.${projection_cell} ";
+	print "$command\n";
+	( $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 ppSkycell: $error_code", $stage_id, $error_code);
+	}
+	# Update database:
+	$command = "$stacktool -addsummary -sass_id $stage_id -projection_cell $projection_cell -path_base $outroot";
+	if (defined($dbname)) {
+	    $command .= " -dbname $dbname";
+	}
+	
+	( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) = run(command => $command, verbose => $verbose);
+	unless ($success) {
+	    $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+	    &my_die("unable to update stack summary: $error_code", $stage_id, $error_code);
+	}
+    }
+    if (scalar (keys %tangents) == 0) {
+	my $projection_cell = 'NULL';
+	$command = "$stacktool -addsummary -sass_id $stage_id -projection_cell $projection_cell -path_base $outroot";
+	if (defined($dbname)) {
+	    $command .= " -dbname $dbname";
+	}
+#	print "$command\n";
+	( $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 update stack summary: $error_code", $stage_id, $error_code);
+	}
+    }	
+
+} #end stack stage
+
+
+sub my_die
+{
+    my $msg = shift; # Warning message on die
+    my $stage = shift; # stage name
+    my $stage_id = shift; #  identifier
+    my $exit_code = shift; # Exit code
+    # outputImage and path_base are globals
+
+    carp($msg);
+    exit $exit_code;
+}
Index: /branches/pap/ippTasks/Makefile.am
===================================================================
--- /branches/pap/ippTasks/Makefile.am	(revision 28483)
+++ /branches/pap/ippTasks/Makefile.am	(revision 28484)
@@ -32,7 +32,9 @@
 	dqstats.pro \
 	science.cleanup.pro \
+	minidvodb.pro \
 	nightly_stacks.pro \
 	lossy_compress.pro \
-	background.pro
+	background.pro \
+	diffphot.pro
 
 other_files = \
Index: /branches/pap/ippTasks/addstar.pro
===================================================================
--- /branches/pap/ippTasks/addstar.pro	(revision 28483)
+++ /branches/pap/ippTasks/addstar.pro	(revision 28484)
@@ -45,7 +45,9 @@
 end
 
+
 # this variable will cycle through the known database names
 $addstar_DB = 0
 $addstar_revert_DB = 0
+
 
 # select images ready for addstar analysis
@@ -64,5 +66,5 @@
 
   task.exec
-    if ($LABEL:n == 0) break
+   # if ($LABEL:n == 0) break
     $run = addtool -pendingexp
     if ($DB:n == 0)
@@ -134,4 +136,7 @@
     book getword addPendingExp $pageName reduction -var REDUCTION
     book getword addPendingExp $pageName dvodb  -var DVODB
+    book getword addPendingExp $pageName minidvodb  -var MINIDVODB
+    book getword addPendingExp $pageName minidvodb_name  -var MINIDVODB_NAME
+    book getword addPendingExp $pageName minidvodb_group  -var MINIDVODB_GROUP
     book getword addPendingExp $pageName image_only -var IMAGE_ONLY
     book getword addPendingExp $pageName dbname -var DBNAME
@@ -164,4 +169,11 @@
       $run = $run --image-only
     end
+    if ("$MINIDVODB" == "T")
+    $run = $run --minidvodb
+    $run = $run --minidvodb_group $MINIDVODB_GROUP
+	if (("$MINIDVODB_NAME" != "NULL") && ("$MINIDVODB_NAME" != "(null)"))
+           $run = $run --minidvodb_name $MINIDVODB_NAME #have addstar_run.pl grab the 'active' name if it is NULL
+	end
+    end 
     
     add_standard_args run
Index: /branches/pap/ippTasks/diff.pro
===================================================================
--- /branches/pap/ippTasks/diff.pro	(revision 28483)
+++ /branches/pap/ippTasks/diff.pro	(revision 28484)
@@ -11,4 +11,5 @@
 book init diffSkyfile
 #book init diffCleanup
+book init diffPendingSummary
 
 ### Database lists
@@ -16,4 +17,5 @@
 $diffAdvance_DB = 0
 $diff_revert_DB = 0
+$diffSummary_DB = 0
 #$diffCleanup_DB = 0
 
@@ -27,4 +29,5 @@
 macro diff.reset
   book init diffSkyfile
+  book init diffPendingSummary
 #  book init diffCleanup
 end
@@ -44,4 +47,10 @@
     active false
   end
+  task diff.summary.load
+    active true
+  end
+  task diff.summary.run
+    active true
+  end
 end
 
@@ -58,4 +67,10 @@
   end
   task diff.revert
+    active false
+  end
+  task diff.summary.load
+    active false
+  end
+  task diff.summary.run
     active false
   end
@@ -193,5 +208,4 @@
     # host anyhost
     # $WORKDIR = $WORKDIR_TEMPLATE
-
     if (($DIFF_MODE == 1)||("$DIFF_MODE" == "NULL")) 
 	$DIFF_TAG = ""
@@ -477,2 +491,150 @@
   end
 end
+
+### Load tasks for doing the diffs
+### Tasks are loaded into diffPendingSkyCell.
+task	       diff.summary.load
+  host         local
+
+  periods      -poll $LOADPOLL
+  periods      -exec $LOADEXEC
+  periods      -timeout 1200
+  npending     1
+
+  stdout NULL
+  stderr $LOGDIR/diff.summary.log
+
+  task.exec
+    if ($LABEL:n == 0) break
+    $run = difftool -tosummary
+    if ($DB:n == 0)
+      option DEFAULT
+    else
+      # save the DB name for the exit tasks
+      option $DB:$diffSummary_DB
+      $run = $run -dbname $DB:$diffSummary_DB
+      $diffSummary_DB ++
+      if ($diffSummary_DB >= $DB:n) set diffSummary_DB = 0
+    end
+    add_poll_args run
+    add_poll_labels run
+    command $run
+  end
+
+  # success
+  task.exit    0
+    # convert 'stdout' to book format
+    # XXX change tess_id to tess_dir when db is updated
+    ipptool2book stdout diffPendingSummary -key diff_id -uniq -setword dbname $options:0 -setword pantaskState INIT
+    if ($VERBOSE > 2)
+      book listbook diffPendingSummary
+    end
+
+    # delete existing entries in the appropriate pantaskStates
+    process_cleanup diffPendingSummary
+  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 calculating the diff overlaps
+### Tasks are taken from diffPendingSkyCell.
+task	       diff.summary.run
+  periods      -poll $RUNPOLL
+  periods      -exec $RUNEXEC
+  periods      -timeout 60
+
+  task.exec
+    # if we are unable to run the 'exec', use a long retry time
+    periods -exec $RUNEXEC
+
+    book npages diffPendingSummary -var N
+    if ($N == 0) break
+    if ($NETWORK == 0) break
+    
+    # look for new images in diffPendingSkyCell (pantaskState == INIT)
+    book getpage diffPendingSummary 0 -var pageName -key pantaskState INIT
+    if ("$pageName" == "NULL") break
+
+    book setword diffPendingSummary $pageName pantaskState RUN
+    book getword diffPendingSummary $pageName diff_id -var DIFF_ID
+    book getword diffPendingSummary $pageName camera -var CAMERA
+    book getword diffPendingSummary $pageName workdir -var WORKDIR_TEMPLATE
+    book getword diffPendingSummary $pageName dbname -var DBNAME
+    book getword diffPendingSummary $pageName tess_id -var TESS_DIR
+    book getword diffPendingSummary $pageName diff_mode -var DIFF_MODE
+    book getword diffPendingSummary $pageName state -var RUN_STATE
+
+    # set the host and workdir based on the skycell hash
+    host anyhost
+    strsub $WORKDIR_TEMPLATE @HOST@.0 $default_host -var WORKDIR
+#    set.workdir.by.skycell $SKYCELL_ID $WORKDIR_TEMPLATE $default_host WORKDIR
+
+#    if ("$PATH_BASE" == "NULL") 
+
+    if (($DIFF_MODE == 1)||("$DIFF_MODE" == "NULL")) 
+	$DIFF_TAG = ""
+    end
+    if ($DIFF_MODE == 2)
+	$DIFF_TAG = "WS."
+    end 
+    if ($DIFF_MODE == 3)
+	$DIFF_TAG = "SW."
+    end
+    if ($DIFF_MODE == 4)
+	$DIFF_TAG = "SS."
+    end
+
+    basename $TESS_DIR -var TESS_ID
+
+    ## generate outroot specific to this exposure
+    sprintf outroot "%s/%s/%s.%sdif.%s.summary" $WORKDIR $TESS_ID $TESS_ID $DIFF_TAG $DIFF_ID
+
+
+    stdout $LOGDIR/diff.summary.log
+    stderr $LOGDIR/diff.summary.log
+
+    $run = skycell_jpeg.pl --stage diff --stage_id $DIFF_ID --camera $CAMERA --outroot $outroot
+    add_standard_args run
+
+    # 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 diffPendingSummary $options:0 $JOB_STATUS
+  end
+
+  # locked list
+  task.exit    crash
+    showcommand crash
+    echo "hostname: $JOB_HOSTNAME"
+    book setword diffPendingSummary $options:0 pantaskState CRASH
+  end
+
+  # operation timed out?
+  task.exit    timeout
+    showcommand timeout
+    book setword diffPendingSummary $options:0 pantaskState TIMEOUT
+  end
+end
Index: /branches/pap/ippTasks/diffphot.pro
===================================================================
--- /branches/pap/ippTasks/diffphot.pro	(revision 28484)
+++ /branches/pap/ippTasks/diffphot.pro	(revision 28484)
@@ -0,0 +1,296 @@
+## diffphot.pro: (re-)photometry of diffs : -*- sh -*-
+
+# test for required global variables
+check.globals
+
+### Initialise the books containing the tasks to do
+book init diffphotRuns
+
+### Database lists
+$diffphotLoad_DB = 0
+$diffphotAdvance_DB = 0
+$diffphotRevert_DB = 0
+
+### Check status of diffing tasks
+macro diffphot.status
+  book listbook diffphotRuns
+end
+
+### Reset diffing tasks
+macro diffphot.reset
+  book init diffphotRuns
+end
+
+### Turn diffing tasks on
+macro diffphot.on
+  task diffphot.load
+    active true
+  end
+  task diffphot.run
+    active true
+  end
+  task diffphot.advance
+    active true
+  end
+  task diffphot.revert
+    active false
+  end
+end
+
+### Turn diffing tasks off
+macro diffphot.off
+  task diffphot.load
+    active false
+  end
+  task diffphot.run
+    active false
+  end
+  task diffphot.advance
+    active false
+  end
+  task diffphot.revert
+    active false
+  end
+end
+
+macro diffphot.revert.on
+  task diffphot.revert
+    active true
+  end
+end
+
+macro diffphot.revert.off
+  task diffphot.revert
+    active false
+  end
+end
+
+
+# Load processing jobs
+task	       diffphot.load
+  host         local
+
+  periods      -poll $LOADPOLL
+  periods      -exec $LOADEXEC
+  periods      -timeout 30
+  npending     1
+
+  stdout NULL
+  stderr $LOGDIR/diffphot.load.log
+
+  task.exec
+    if ($LABEL:n == 0) break
+    $run = diffphottool -pending
+    if ($DB:n == 0)
+      option DEFAULT
+    else
+      # save the DB name for the exit tasks
+      option $DB:$diffphotLoad_DB
+      $run = $run -dbname $DB:$diffphotLoad_DB
+      $diffphotLoad_DB ++
+      if ($diffphotLoad_DB >= $DB:n) set diffphotLoad_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 diffphotRuns -key diff_phot_id:skycell_id -uniq -setword dbname $options:0 -setword pantaskState INIT
+    if ($VERBOSE > 2)
+      book listbook diffphotRuns
+    end
+
+    # delete existing entries in the appropriate pantaskStates
+    process_cleanup diffphotRuns
+  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 calculating the diff overlaps
+### Tasks are taken from diffSkyfile.
+task	       diffphot.run
+  periods      -poll $RUNPOLL
+  periods      -exec $RUNEXEC
+  periods      -timeout 60
+
+  task.exec
+    # if we are unable to run the 'exec', use a long retry time
+    periods -exec $RUNEXEC
+
+    book npages diffphotRuns -var N
+    if ($N == 0) break
+    if ($NETWORK == 0) break
+    if ($BURNTOOLING == 1) break
+
+    book getpage diffphotRuns 0 -var pageName -key pantaskState INIT
+    if ("$pageName" == "NULL") break
+
+    book setword diffphotRuns $pageName pantaskState RUN
+    book getword diffphotRuns $pageName diff_phot_id -var DIFF_PHOT_ID
+    book getword diffphotRuns $pageName skycell_id -var SKYCELL_ID
+    book getword diffphotRuns $pageName tess_id -var TESS_DIR
+    book getword diffphotRuns $pageName workdir -var WORKDIR_TEMPLATE
+    book getword diffphotRuns $pageName state -var RUN_STATE
+    book getword diffphotRuns $pageName dbname -var DBNAME
+    book getword diffphotRuns $pageName reduction -var REDUCTION
+
+    # 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.dp.%s" $WORKDIR $TESS_ID $SKYCELL_ID $TESS_ID $SKYCELL_ID $DIFF_PHOT_ID
+
+    stdout $LOGDIR/diffphot.run.log
+    stderr $LOGDIR/diffphot.run.log
+
+    $run = diffphot.pl --threads @MAX_THREADS@ --diff_phot_id $DIFF_PHOT_ID --skycell_id $SKYCELL_ID --outroot $outroot --redirect-output
+    if ("$REDUCTION" != "NULL")
+      $run = $run --reduction $REDUCTION
+    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
+    periods -exec 0.05
+    command $run
+  end
+
+  # default exit status
+  task.exit    default
+    process_exit diffphotRuns $options:0 $JOB_STATUS
+  end
+
+  # locked list
+  task.exit    crash
+    showcommand crash
+    echo "hostname: $JOB_HOSTNAME"
+    book setword diffphotRuns $options:0 pantaskState CRASH
+  end
+
+  # operation timed out?
+  task.exit    timeout
+    showcommand timeout
+    book setword diffphotRuns $options:0 pantaskState TIMEOUT
+  end
+end
+
+
+# Advance runs which have completed
+task	       diffphot.advance
+  host         local
+
+  periods      -poll $LOADPOLL
+#  periods      -exec $LOADEXEC
+  periods      -exec 30
+  periods      -timeout 60
+  npending     1
+
+  stdout NULL
+  stderr $LOGDIR/diffphot.advance.log
+
+  task.exec
+    if ($LABEL:n == 0) break
+    $run = diffphottool -advance
+    if ($DB:n == 0)
+      option DEFAULT
+    else
+      # save the DB name for the exit tasks
+      option $DB:$diffphotAdvance_DB
+      $run = $run -dbname $DB:$diffphotAdvance_DB
+      $diffphotAdvance_DB ++
+      if ($diffphotAdvance_DB >= $DB:n) set diffphotAdvance_DB = 0
+    end
+    add_poll_args run
+    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
+
+
+# Revert runs that failed
+task diffphot.revert
+  host         local
+
+  periods      -poll 60.0
+  periods      -exec 1800.0
+  periods      -timeout 120.0
+  npending     1
+  active false
+
+  stdout NULL
+  stderr $LOGDIR/diffphot.revert.log
+
+  task.exec
+    if ($LABEL:n == 0) break
+    $run = diffphottool -revert
+    if ($DB:n == 0)
+      option DEFAULT
+    else
+      # save the DB name for the exit tasks
+      option $DB:$diffphotRevert_DB
+      $run = $run -dbname $DB:$diffphotRevert_DB
+      $diffphotRevert_DB ++
+      if ($diffphotRevert_DB >= $DB:n) set diffphotRevert_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/pap/ippTasks/ipphosts.mhpcc.config
===================================================================
--- /branches/pap/ippTasks/ipphosts.mhpcc.config	(revision 28483)
+++ /branches/pap/ippTasks/ipphosts.mhpcc.config	(revision 28484)
@@ -1,103 +1,142 @@
 ipphosts MULTI
 
+# In this file, we assign all storage hosts to chip and skycells any
+# hosts which are having problems are removed as active volumes from
+# nebulous.  data targeted to those machines are simply re-distributed
+# 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
 ipphosts METADATA
   camera STR skycell
-  count S32  46
-  sky00 STR  ipp006
-  sky01 STR  ipp007
-  sky02 STR  ipp008 
-  sky03 STR  ipp009
-  sky04 STR  ipp010
-  sky05 STR  ipp011
-  sky06 STR  ipp012
-  sky07 STR  ipp013
-#  sky08 STR  ipp014
-  sky08 STR  ipp047
-  sky09 STR  ipp015
-#  sky10 STR  ipp016
-  sky10 STR  ipp042
-  sky11 STR  ipp017
-  sky12 STR  ipp018 
-  sky13 STR  ipp019
-  sky14 STR  ipp020
-  sky15 STR  ipp021
-  sky16 STR  ipp023
-  sky17 STR  ipp024
-  sky18 STR  ipp025
-  sky19 STR  ipp026
-  sky20 STR  ipp027
-  sky21 STR  ipp028
-  sky22 STR  ipp029 
-  sky23 STR  ipp030
-  sky24 STR  ipp031
-  sky25 STR  ipp032
-  sky26 STR  ipp033
-  sky27 STR  ipp034
-  sky28 STR  ipp035
-  sky29 STR  ipp036
-  sky30 STR  ipp038
-  sky31 STR  ipp039
-  sky32 STR  ipp040 
-  sky33 STR  ipp041
-  sky34 STR  ipp042
-  sky35 STR  ipp043
-  sky36 STR  ipp044
-  sky37 STR  ipp045
-  sky38 STR  ipp046
-  sky39 STR  ipp047
-  sky40 STR  ipp048
-  sky41 STR  ipp049
-  sky42 STR  ipp050 
-  sky43 STR  ipp051
-  sky44 STR  ipp052
-  sky45 STR  ipp053
+  count S32  79
+  sky00 STR  ipp005
+  sky01 STR  ipp006 
+  sky02 STR  ipp007
+  sky03 STR  ipp008
+  sky04 STR  ipp009
+  sky05 STR  ipp010
+  sky06 STR  ipp011
+  sky07 STR  ipp012
+  sky08 STR  ipp013
+  sky09 STR  ipp014
+  sky10 STR  ipp015
+  sky11 STR  ipp016 
+  sky12 STR  ipp017
+  sky13 STR  ipp018
+  sky14 STR  ipp019
+  sky15 STR  ipp020
+  sky16 STR  ipp021
+  sky17 STR  ipp023
+  sky18 STR  ipp024
+  sky19 STR  ipp025
+  sky20 STR  ipp026
+  sky21 STR  ipp027 
+  sky22 STR  ipp028
+  sky23 STR  ipp029
+  sky24 STR  ipp030
+  sky25 STR  ipp031
+  sky26 STR  ipp032
+  sky27 STR  ipp033
+  sky28 STR  ipp034
+  sky29 STR  ipp035
+  sky30 STR  ipp036
+  sky31 STR  ipp037 
+  sky32 STR  ipp038
+  sky33 STR  ipp039
+  sky34 STR  ipp040
+  sky35 STR  ipp041
+  sky36 STR  ipp042
+  sky37 STR  ipp043
+  sky38 STR  ipp044
+  sky39 STR  ipp045
+  sky40 STR  ipp046
+  sky41 STR  ipp047 
+  sky42 STR  ipp048
+  sky43 STR  ipp049
+  sky44 STR  ipp050
+  sky45 STR  ipp051
+  sky46 STR  ipp052
+  sky47 STR  ipp053
+  sky48 STR  ipp023
+  sky49 STR  ipp024
+  sky50 STR  ipp025
+  sky51 STR  ipp026
+  sky52 STR  ipp027 
+  sky53 STR  ipp028
+  sky54 STR  ipp029
+  sky55 STR  ipp030
+  sky56 STR  ipp031
+  sky57 STR  ipp032
+  sky58 STR  ipp033
+  sky59 STR  ipp034
+  sky60 STR  ipp035
+  sky61 STR  ipp036
+  sky62 STR  ipp037 
+  sky63 STR  ipp038
+  sky64 STR  ipp039
+  sky65 STR  ipp040
+  sky66 STR  ipp041
+  sky67 STR  ipp042
+  sky68 STR  ipp043
+  sky69 STR  ipp044
+  sky70 STR  ipp045
+  sky71 STR  ipp046
+  sky72 STR  ipp047 
+  sky73 STR  ipp048
+  sky74 STR  ipp049
+  sky75 STR  ipp050
+  sky76 STR  ipp051
+  sky77 STR  ipp052
+  sky78 STR  ipp053
 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...
 
 ipphosts METADATA
   camera STR GPC1
 
-#  XY01  STR  ipp014
-#  XY02  STR  ipp014
-  XY01  STR  ipp047
-  XY02  STR  ipp047
-  XY03  STR  ipp038
+  XY01  STR  ipp037
+  XY02  STR  ipp005
+  XY03  STR  ipp006
   XY04  STR  ipp038
 
-  XY05  STR  ipp023
+  XY05  STR  ipp007
   XY06  STR  ipp023
-  XY10  STR  ipp039
+  XY10  STR  ipp008
   XY11  STR  ipp039
 
-  XY12  STR  ipp024
+  XY12  STR  ipp009
   XY13  STR  ipp024
-  XY14  STR  ipp040
+  XY14  STR  ipp010
   XY15  STR  ipp040
 
-  XY16  STR  ipp026
+  XY16  STR  ipp011
   XY17  STR  ipp026
-  XY20  STR  ipp041
+  XY20  STR  ipp012
   XY21  STR  ipp041
 
-  XY22  STR  ipp042
+  XY22  STR  ipp013
   XY23  STR  ipp042
-  XY24  STR  ipp043
+  XY24  STR  ipp014
   XY25  STR  ipp043
 
-  XY26  STR  ipp028
+  XY26  STR  ipp027
   XY27  STR  ipp028
-  XY30  STR  ipp044
+  XY30  STR  ipp016
   XY31  STR  ipp044
 
-  XY32  STR  ipp029
+  XY32  STR  ipp017
   XY33  STR  ipp029
-  XY34  STR  ipp045
+  XY34  STR  ipp018
   XY35  STR  ipp045
 
-  XY36  STR  ipp030
+  XY36  STR  ipp019
   XY37  STR  ipp030
-  XY40  STR  ipp046
+  XY40  STR  ipp020
   XY41  STR  ipp046
 
-  XY42  STR  ipp031
+  XY42  STR  ipp021
   XY43  STR  ipp031
   XY44  STR  ipp047
@@ -129,5 +168,5 @@
   XY72  STR  ipp052
 
-  XY73  STR  ipp015
+  XY73  STR  ipp053
   XY74  STR  ipp015
   XY75  STR  ipp025
@@ -138,77 +177,75 @@
   camera STR gpc1
 
-#  ota01  STR  ipp014
-#  ota02  STR  ipp014
-  ota01  STR  ipp047
-  ota02  STR  ipp047
-  ota03  STR  ipp038
+  ota01  STR  ipp037
+  ota02  STR  ipp005
+  ota03  STR  ipp006
   ota04  STR  ipp038
-
-  ota05  STR  ipp023
+	            
+  ota05  STR  ipp007
   ota06  STR  ipp023
-  ota10  STR  ipp039
+  ota10  STR  ipp008
   ota11  STR  ipp039
-
-  ota12  STR  ipp024
+	            
+  ota12  STR  ipp009
   ota13  STR  ipp024
-  ota14  STR  ipp040
+  ota14  STR  ipp010
   ota15  STR  ipp040
-
-  ota16  STR  ipp026
+	            
+  ota16  STR  ipp011
   ota17  STR  ipp026
-  ota20  STR  ipp041
+  ota20  STR  ipp012
   ota21  STR  ipp041
-
-  ota22  STR  ipp042
+	            
+  ota22  STR  ipp013
   ota23  STR  ipp042
-  ota24  STR  ipp043
+  ota24  STR  ipp014
   ota25  STR  ipp043
-
-  ota26  STR  ipp028
+	            
+  ota26  STR  ipp027
   ota27  STR  ipp028
-  ota30  STR  ipp044
+  ota30  STR  ipp016
   ota31  STR  ipp044
-
-  ota32  STR  ipp029
+	            
+  ota32  STR  ipp017
   ota33  STR  ipp029
-  ota34  STR  ipp045
+  ota34  STR  ipp018
   ota35  STR  ipp045
-
-  ota36  STR  ipp030
+	            
+  ota36  STR  ipp019
   ota37  STR  ipp030
-  ota40  STR  ipp046
+  ota40  STR  ipp020
   ota41  STR  ipp046
-
-  ota42  STR  ipp031
+	            
+  ota42  STR  ipp021
   ota43  STR  ipp031
   ota44  STR  ipp047
   ota45  STR  ipp047
-
+	            
   ota46  STR  ipp032
   ota47  STR  ipp032
   ota50  STR  ipp048
   ota51  STR  ipp048
-
+	            
   ota52  STR  ipp033
   ota53  STR  ipp033
   ota54  STR  ipp049
   ota55  STR  ipp049
-
+	            
   ota56  STR  ipp034
   ota57  STR  ipp034
   ota60  STR  ipp050
   ota61  STR  ipp050
-
+	            
   ota62  STR  ipp035
   ota63  STR  ipp035
   ota64  STR  ipp051
   ota65  STR  ipp051
-
+	            
   ota66  STR  ipp036
   ota67  STR  ipp036
   ota71  STR  ipp052
   ota72  STR  ipp052
-
-  ota73  STR  ipp015
+	            
+  ota73  STR  ipp053
   ota74  STR  ipp015
   ota75  STR  ipp025
@@ -217,34 +254,35 @@
 
 # this list is no longer used
-ipphosts METADATA
-  camera STR distribution
-  count  S32  26
-  0      STR ipp021
-  1      STR ipp023
-  2      STR ipp024
-  3      STR ipp026
-  4      STR ipp028
-  5      STR ipp029
-  6      STR ipp030
-  7      STR ipp031
-  8      STR ipp032
-  9      STR ipp033
- 10      STR ipp034
- 11      STR ipp035
- 12      STR ipp036
- 13      STR ipp038
- 14      STR ipp039
- 15      STR ipp040
- 16      STR ipp041
- 17      STR ipp043
- 18      STR ipp044
- 19      STR ipp045
- 20      STR ipp046
- 21      STR ipp047
- 22      STR ipp048
- 23      STR ipp050
- 24      STR ipp051
- 25      STR ipp052
-END
+# XXX : delete if this does not cause trouble
+## ipphosts METADATA
+##   camera STR distribution
+##   count  S32  26
+##   0      STR ipp021
+##   1      STR ipp023
+##   2      STR ipp024
+##   3      STR ipp026
+##   4      STR ipp028
+##   5      STR ipp029
+##   6      STR ipp030
+##   7      STR ipp031
+##   8      STR ipp032
+##   9      STR ipp033
+##  10      STR ipp034
+##  11      STR ipp035
+##  12      STR ipp036
+##  13      STR ipp038
+##  14      STR ipp039
+##  15      STR ipp040
+##  16      STR ipp041
+##  17      STR ipp043
+##  18      STR ipp044
+##  19      STR ipp045
+##  20      STR ipp046
+##  21      STR ipp047
+##  22      STR ipp048
+##  23      STR ipp050
+##  24      STR ipp051
+##  25      STR ipp052
+## END
 
 ipphosts METADATA
@@ -252,77 +290,75 @@
   count S32   60
 
-#  XY01  STR  ipp014
-#  XY02  STR  ipp014
-  XY01  STR  ipp047
-  XY02  STR  ipp047
-  XY03  STR  ipp038
+  XY01  STR  ipp037
+  XY02  STR  ipp005
+  XY03  STR  ipp006
   XY04  STR  ipp038
-
-  XY05  STR  ipp023
+	           
+  XY05  STR  ipp007
   XY06  STR  ipp023
-  XY10  STR  ipp039
+  XY10  STR  ipp008
   XY11  STR  ipp039
-
-  XY12  STR  ipp024
+	           
+  XY12  STR  ipp009
   XY13  STR  ipp024
-  XY14  STR  ipp040
+  XY14  STR  ipp010
   XY15  STR  ipp040
-
-  XY16  STR  ipp026
+	           
+  XY16  STR  ipp011
   XY17  STR  ipp026
-  XY20  STR  ipp041
+  XY20  STR  ipp012
   XY21  STR  ipp041
-
-  XY22  STR  ipp042
+	           
+  XY22  STR  ipp013
   XY23  STR  ipp042
-  XY24  STR  ipp043
+  XY24  STR  ipp014
   XY25  STR  ipp043
-
-  XY26  STR  ipp028
+	           
+  XY26  STR  ipp027
   XY27  STR  ipp028
-  XY30  STR  ipp044
+  XY30  STR  ipp016
   XY31  STR  ipp044
-
-  XY32  STR  ipp029
+	           
+  XY32  STR  ipp017
   XY33  STR  ipp029
-  XY34  STR  ipp045
+  XY34  STR  ipp018
   XY35  STR  ipp045
-
-  XY36  STR  ipp030
+	           
+  XY36  STR  ipp019
   XY37  STR  ipp030
-  XY40  STR  ipp046
+  XY40  STR  ipp020
   XY41  STR  ipp046
-
-  XY42  STR  ipp031
+	           
+  XY42  STR  ipp021
   XY43  STR  ipp031
   XY44  STR  ipp047
   XY45  STR  ipp047
-
+	           
   XY46  STR  ipp032
   XY47  STR  ipp032
   XY50  STR  ipp048
   XY51  STR  ipp048
-
+	           
   XY52  STR  ipp033
   XY53  STR  ipp033
   XY54  STR  ipp049
   XY55  STR  ipp049
-
+	           
   XY56  STR  ipp034
   XY57  STR  ipp034
   XY60  STR  ipp050
   XY61  STR  ipp050
-
+	           
   XY62  STR  ipp035
   XY63  STR  ipp035
   XY64  STR  ipp051
   XY65  STR  ipp051
-
+	           
   XY66  STR  ipp036
   XY67  STR  ipp036
   XY71  STR  ipp052
   XY72  STR  ipp052
-
-  XY73  STR  ipp015
+	           
+  XY73  STR  ipp053
   XY74  STR  ipp015
   XY75  STR  ipp025
@@ -333,51 +369,82 @@
   camera STR dist_skycell
   count S32  46
-  sky00 STR  ipp006
-  sky01 STR  ipp007
-  sky02 STR  ipp008 
-  sky03 STR  ipp009
-  sky04 STR  ipp010
-  sky05 STR  ipp011
-  sky06 STR  ipp012
-  sky07 STR  ipp013
-#  sky08 STR  ipp014
-  sky08 STR  ipp047
-  sky09 STR  ipp015
-#  sky10 STR  ipp016
-  sky10 STR  ipp042
-  sky11 STR  ipp017
-  sky12 STR  ipp018 
-  sky13 STR  ipp019
-  sky14 STR  ipp020
-  sky15 STR  ipp021
-  sky16 STR  ipp023
-  sky17 STR  ipp024
-  sky18 STR  ipp025
-  sky19 STR  ipp026
-  sky20 STR  ipp027
-  sky21 STR  ipp028
-  sky22 STR  ipp029 
-  sky23 STR  ipp030
-  sky24 STR  ipp031
-  sky25 STR  ipp032
-  sky26 STR  ipp033
-  sky27 STR  ipp034
-  sky28 STR  ipp035
-  sky29 STR  ipp036
-  sky30 STR  ipp038
-  sky31 STR  ipp039
-  sky32 STR  ipp040 
-  sky33 STR  ipp041
-  sky34 STR  ipp042
-  sky35 STR  ipp043
-  sky36 STR  ipp044
-  sky37 STR  ipp045
-  sky38 STR  ipp046
-  sky39 STR  ipp047
-  sky40 STR  ipp048
-  sky41 STR  ipp049
-  sky42 STR  ipp050 
-  sky43 STR  ipp051
-  sky44 STR  ipp052
-  sky45 STR  ipp053
+  sky00 STR  ipp005
+  sky01 STR  ipp006 
+  sky02 STR  ipp007
+  sky03 STR  ipp008
+  sky04 STR  ipp009
+  sky05 STR  ipp010
+  sky06 STR  ipp011
+  sky07 STR  ipp012
+  sky08 STR  ipp013
+  sky09 STR  ipp014
+  sky10 STR  ipp015
+  sky11 STR  ipp016 
+  sky12 STR  ipp017
+  sky13 STR  ipp018
+  sky14 STR  ipp019
+  sky15 STR  ipp020
+  sky16 STR  ipp021
+  sky17 STR  ipp023
+  sky18 STR  ipp024
+  sky19 STR  ipp025
+  sky20 STR  ipp026
+  sky21 STR  ipp027 
+  sky22 STR  ipp028
+  sky23 STR  ipp029
+  sky24 STR  ipp030
+  sky25 STR  ipp031
+  sky26 STR  ipp032
+  sky27 STR  ipp033
+  sky28 STR  ipp034
+  sky29 STR  ipp035
+  sky30 STR  ipp036
+  sky31 STR  ipp037 
+  sky32 STR  ipp038
+  sky33 STR  ipp039
+  sky34 STR  ipp040
+  sky35 STR  ipp041
+  sky36 STR  ipp042
+  sky37 STR  ipp043
+  sky38 STR  ipp044
+  sky39 STR  ipp045
+  sky40 STR  ipp046
+  sky41 STR  ipp047 
+  sky42 STR  ipp048
+  sky43 STR  ipp049
+  sky44 STR  ipp050
+  sky45 STR  ipp051
+  sky46 STR  ipp052
+  sky47 STR  ipp053
+  sky48 STR  ipp023
+  sky49 STR  ipp024
+  sky50 STR  ipp025
+  sky51 STR  ipp026
+  sky52 STR  ipp027 
+  sky53 STR  ipp028
+  sky54 STR  ipp029
+  sky55 STR  ipp030
+  sky56 STR  ipp031
+  sky57 STR  ipp032
+  sky58 STR  ipp033
+  sky59 STR  ipp034
+  sky60 STR  ipp035
+  sky61 STR  ipp036
+  sky62 STR  ipp037 
+  sky63 STR  ipp038
+  sky64 STR  ipp039
+  sky65 STR  ipp040
+  sky66 STR  ipp041
+  sky67 STR  ipp042
+  sky68 STR  ipp043
+  sky69 STR  ipp044
+  sky70 STR  ipp045
+  sky71 STR  ipp046
+  sky72 STR  ipp047 
+  sky73 STR  ipp048
+  sky74 STR  ipp049
+  sky75 STR  ipp050
+  sky76 STR  ipp051
+  sky77 STR  ipp052
+  sky78 STR  ipp053
 END
Index: /branches/pap/ippTasks/minidvodb.pro
===================================================================
--- /branches/pap/ippTasks/minidvodb.pro	(revision 28484)
+++ /branches/pap/ippTasks/minidvodb.pro	(revision 28484)
@@ -0,0 +1,734 @@
+## 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($?haveBooks))
+ book create MINIDVODB
+ book create MINIDVODB_MERGE
+ book create MINIDVODB_CREATE
+ book create MINIDVODB_ACTIVE   
+ $haveBooks = TRUE
+end
+
+$MINIDVODB_DB = 0
+
+$MINIDVODB_WAIT_DB = 0
+$MINIDVODB_MERGE_DB = 0
+$MINIDVODB_CREATE_DB = 0
+$MINIDVODB_ACTIVE_DB = 0
+
+book init minidvodbWaitlist
+book init minidvodbMergelist
+book init minidvodbCreatelist
+book init minidvodbActivelist
+
+macro minidvodb.create.status
+  book listbook minidvodbCreatelist
+end
+
+macro minidvodb.create.reset
+  book init minidvodbCreatelist
+end
+
+macro minidvodb.active.status
+  book listbook minidvodbActivelist
+end
+
+macro minidvodb.active.reset
+  book init minidvodbActivelist
+end
+
+
+macro minidvodb.wait.status
+  book listbook minidvodbWaitlist
+end
+
+macro minidvodb.wait.reset
+  book init minidvodbWaitlist
+end
+
+macro minidvodb.merge.status
+  book listbook minidvodbMergelist
+end
+
+macro minidvodb.merge.reset
+  book init minidvodbMergelist
+end
+
+#this is the create task, there is no load, it just runs
+macro minidvodb.create.on
+    task minidvodb.create
+    active true
+  end
+end
+macro minidvodb.create.off
+   task minidvodb.create
+    active false
+  end
+end
+##these are the tasks that flip from new -> active
+macro minidvodb.active.on
+  task minidvodb.active.load
+    active true
+  end
+  task minidvodb.active.run
+    active true
+  end  
+end
+macro minidvodb.active.off
+  task minidvodb.active.load
+    active false
+  end
+  task minidvodb.active.run
+    active false
+  end  
+end
+
+##these are the tasks that check to see if addRun processing is finished
+##for a minidvodb in state wait
+macro minidvodb.wait.on
+  task minidvodb.wait.load
+    active true
+  end
+  task minidvodb.wait.run
+    active true
+  end  
+end
+macro minidvodb.wait.off
+  task minidvodb.wait.load
+    active false
+  end
+  task minidvodb.wait.run
+    active false
+  end  
+end
+
+##these merge the dbs
+
+macro minidvodb.merge.on
+  task minidvodb.merge.load
+    active true
+  end
+  task minidvodb.merge.run
+    active true
+  end  
+end
+macro minidvodb.merge.off
+  task minidvodb.merge.load
+    active false
+  end
+  task minidvodb.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.minidvodb
+  if ($0 != 6)
+    echo "USAGE: add.minidvodb (minidvodb_group) (minidvodb) (dvodb) (interval days) (camera)"
+    break
+  end
+
+  #wait - shoudl be renamed MINIDVODB_WAIT
+  book newpage MINIDVODB $1
+  book setword MINIDVODB $1 MINIDVODB_GROUP $1
+  book setword MINIDVODB $1 DVODB $3
+  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
+  #active  
+  book newpage MINIDVODB_ACTIVE $1
+  book setword MINIDVODB_ACTIVE $1 MINIDVODB_GROUP $1
+  book setword MINIDVODB_ACTIVE $1 DVODB $3
+  book setword MINIDVODB_ACTIVE $1 STATE PENDING
+
+  #create  note that camera should be GPC1 for it to work. I couldn't figure out how to easily get this out.
+  book newpage MINIDVODB_CREATE $1
+  book setword MINIDVODB_CREATE $1 MINIDVODB_GROUP $1
+  book setword MINIDVODB_CREATE $1 MINIDVODB $2
+  book setword MINIDVODB_CREATE $1 DVODB $3
+  book setword MINIDVODB_CREATE $1 DVODB_DAYS $4
+  book setword MINIDVODB_CREATE $1 CAMERA $5  
+  book setword MINIDVODB_CREATE $1 STATE PENDING  
+end
+
+macro del.minidvodb
+  if ($0 != 2)
+    echo "USAGE: del.minidvodb (minidvodb)"
+    break
+  end
+  book delpage MINIDVODB $1
+  book delpage MINIDVODB_MERGE $1
+  book delpage MINIDVODB_CREATE $1  
+  book delpage MINIDVODB_ACTIVE $1
+end
+
+macro show.minidvodb
+  if ($0 != 1)
+    echo "USAGE: show.minidvodb"
+    break
+  end
+  echo "minidvodb wait"
+  book listbook MINIDVODB
+  echo "minidvodb merge"
+  book listbook MINIDVODB_MERGE
+  echo "minidvodb create"
+  book listbook MINIDVODB_CREATE 
+  echo "minidvodb_active"
+  book listbook MINIDVODB_ACTIVE
+end
+
+
+
+
+
+
+
+
+task           minidvodb.wait.load
+  host         local
+
+  periods      -poll $LOADPOLL
+  periods      -exec $LOADEXEC
+  periods      -timeout 30
+  npending     1
+
+  stdout NULL
+  stderr $LOGDIR/minidvodb.wait.load.log
+
+  task.exec
+    book npages MINIDVODB -var N
+    if ($N == 0)
+#      echo "No labels for processing"
+      break
+    endif
+
+    book getpage MINIDVODB 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 $i -var minidvodb_group
+        book setword MINIDVODB $minidvodb_group STATE NEW
+      end
+      book getpage MINIDVODB 0 -var minidvodb_group -key STATE NEW
+
+      # Select different database
+      $MINIDVODB_DB ++
+      if ($MINIDVODB_DB >= $DB:n) set MINIDVODB_DB = 0
+    end
+#using check as opposed to list because it sees if it is done with the addRun state yet.  
+    book setword MINIDVODB $minidvodb_group STATE DONE
+    $run = addtool -checkminidvodbrunaddrun -state waiting
+    $run = $run -minidvodb_group $minidvodb_group
+    if ($DB:n == 0)
+      option DEFAULT
+    else
+      # save the DB name for the exit tasks
+      option $DB:$MINIDVODB_DB
+      $run = $run -dbname $DB:$MINIDVODB_DB
+      $MINIDVODB_DB ++
+      if ($MINIDVODB_DB >= $DB:n) set MINIDVODB_DB = 0
+    end
+    add_poll_args run
+   
+    command $run
+  end
+
+  # success
+  task.exit    0
+    # convert 'stdout' to book format
+    ipptool2book stdout minidvodbWaitlist -key minidvodb_id -uniq -setword dbname $options:0 -setword pantaskState INIT
+    if ($VERBOSE > 2)
+      book listbook minidvodbWaitlist
+    end
+    # delete existing entries in the appropriate pantaskStates
+    process_cleanup minidvodbWaitlist
+  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           minidvodb.wait.run
+  periods      -poll $RUNPOLL
+  periods      -exec $RUNEXEC
+  periods      -timeout 60
+
+  task.exec
+    # if we are unable to run the 'exec', use a long retry time
+    periods -exec $RUNEXEC
+
+    book npages minidvodbWaitlist -var N
+    if ($N == 0) break
+   
+    # look for new images in minidvodbWaitlist (pantaskState == INIT)
+    book getpage minidvodbWaitlist 0 -var pageName -key pantaskState INIT
+    if ("$pageName" == "NULL") break
+
+    book setword minidvodbWaitlist $pageName pantaskState RUN
+    book getword minidvodbWaitlist $pageName minidvodb_id -var MINIDVODB_ID
+    book getword minidvodbWaitlist $pageName state -var STATE
+    stdout $LOGDIR/minidvodb.wait.run.log
+    stderr $LOGDIR/minidvodb.wait.run.log
+    
+    $run = addtool -updateminidvodbrun -minidvodb_id $MINIDVODB_ID -set_state to_be_merged  
+    
+   
+if ($DB:n == 0)
+      option DEFAULT
+    else
+      # save the DB name for the exit tasks
+      option $DB:$MINIDVODB_DB
+      $run = $run -dbname $DB:$MINIDVODB_DB
+      $MINIDVODB_DB ++
+      if ($MINIDVODB_DB >= $DB:n) set MINIDVODB_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 minidvodbWaitlist $options:0 $JOB_STATUS
+  end
+
+  # locked list
+  task.exit    crash
+    echo "hostname: $JOB_HOSTNAME"
+    process_exit minidvodbWaitlist $options:0 $EXIT_CRASH_ERR
+  end
+
+  # operation timed out?
+    task.exit    timeout
+	showcommand timeout
+    book setword minidvodbWaitlist $options:0 pantaskState TIMEOUT
+  end
+end
+
+
+
+task           minidvodb.merge.load
+  host         local
+
+  periods      -poll $LOADPOLL
+  periods      -exec $LOADEXEC
+  periods      -timeout 30
+  npending     1
+
+  stdout NULL
+  stderr $LOGDIR/minidvodb.merge.load.log
+
+  task.exec
+    book npages MINIDVODB_MERGE -var N
+    if ($N == 0)
+#      echo "No labels for processing"
+      break
+    endif
+
+    book getpage MINIDVODB_MERGE 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
+      end
+      book getpage MINIDVODB_MERGE 0 -var minidvodb_group -key STATE NEW
+
+      # Select different database
+      $MINIDVODB_DB ++
+      if ($MINIDVODB_DB >= $DB:n) set MINIDVODB_DB = 0
+    end
+    #finds the minidvodbs in a state of 'to_be_merged' 
+    book setword MINIDVODB_MERGE $minidvodb_group STATE DONE
+    $run = addtool -listminidvodbrun -state to_be_merged -limit 1
+    $run = $run -minidvodb_group $minidvodb_group
+    if ($DB:n == 0)
+      option DEFAULT
+    else
+      # save the DB name for the exit tasks
+      option $DB:$MINIDVODB_DB
+      $run = $run -dbname $DB:$MINIDVODB_DB
+      $MINIDVODB_DB ++
+      if ($MINIDVODB_DB >= $DB:n) set MINIDVODB_DB = 0
+    end
+    add_poll_args run
+   
+    command $run
+  end
+
+  # success
+  task.exit    0
+    # convert 'stdout' to book format
+    ipptool2book stdout minidvodbMergelist -key minidvodb_id -uniq -setword dbname $options:0 -setword pantaskState INIT
+    if ($VERBOSE > 2)
+      book listbook minidvodbMergelist
+    end
+    # delete existing entries in the appropriate pantaskStates
+    process_cleanup minidvodbMergelist
+  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           minidvodb.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 minidvodbMergelist -var N
+    if ($N == 0) break
+    
+    # look for new images in minidvodbWaitlist (pantaskState == INIT)
+    book getpage minidvodbMergelist 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
+    
+  if ($DB:n == 0)
+      option DEFAULT
+    else
+      # save the DB name for the exit tasks
+      option $DB:$MINIDVODB_DB
+      $run = $run --dbname $DB:$MINIDVODB_DB
+      $MINIDVODB_DB ++
+      if ($MINIDVODB_DB >= $DB:n) set MINIDVODB_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 minidvodbMergelist $options:0 $JOB_STATUS
+  end
+
+  # locked list
+  task.exit    crash
+  
+    echo "hostname: $JOB_HOSTNAME"
+
+    # Set a fault code in the database
+    
+    process_exit minidvodbMergelist $options:0 $EXIT_CRASH_ERR
+  end
+
+  # operation timed out?
+    task.exit    timeout
+	showcommand timeout
+    book setword minidvodbMergelist $options:0 pantaskState TIMEOUT
+  end
+end
+
+
+#this is the complicated script - it creates the minidvodbRuns
+#since none may exist by default, I decided not to have a load.task
+#it calls a perl script, which creates a new one if:
+#there is none in an active state (true if there is none at all)
+#the current one is older than the dvodb_age
+#there are none in new (we have a task that moves it to 'active')
+#the current active one has > 30000 add_ids in it
+
+#also confusing: it succeeds if it doesn't create it (if it doesn't want to create one)
+#and it succeeds if it creates one. (otherwise there would be a lot of false failures,
+# like in replication)
+#it fails if it has problems with one of the addtool commands in the script.
+
+task           minidvodb.create
+  host         local
+
+  periods      -poll $LOADPOLL
+  periods      -exec $LOADEXEC
+  periods      -timeout 240
+  npending     1
+
+  stdout NULL
+  stderr $LOGDIR/minidvodb.create.log
+
+  task.exec
+    
+ periods      -poll 60
+#wait a bit before trying again
+
+    book npages MINIDVODB_CREATE -var N
+    if ($N == 0)
+#      echo "No labels for processing"
+      break
+    endif
+
+    book getpage MINIDVODB_CREATE 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_CREATE $i -var minidvodb_group
+        book setword MINIDVODB_CREATE $minidvodb_group STATE NEW
+      end
+       book getpage MINIDVODB_CREATE 0 -var minidvodb_group -key STATE NEW
+      # Select different database
+      $MINIDVODB_DB ++
+      if ($MINIDVODB_DB >= $DB:n) set MINIDVODB_DB = 0
+    end
+     
+    book setword MINIDVODB_CREATE $minidvodb_group STATE DONE
+    book getword MINIDVODB_CREATE $minidvodb_group MINIDVODB -var minidvodb 
+    book getword MINIDVODB_CREATE $minidvodb_group DVODB -var dvodb 
+    book getword MINIDVODB_CREATE $minidvodb_group DVODB_DAYS -var dvodb_days
+    book getword MINIDVODB_CREATE $minidvodb_group CAMERA -var camera
+    $run = minidvodb_createdb.pl --camera $camera --outroot $dvodb --dvodb $dvodb --minidvodb $minidvodb --minidvodb_group $minidvodb_group --interval $dvodb_days
+ 
+    if ($DB:n == 0)
+      option DEFAULT
+    else
+      # save the DB name for the exit tasks
+      option $DB:$MINIDVODB_DB
+      $run = $run --dbname $DB:$MINIDVODB_DB
+      $MINIDVODB_DB ++
+      if ($MINIDVODB_DB >= $DB:n) set MINIDVODB_DB = 0
+    end
+    command $run
+  end
+
+  # success
+  task.exit    0
+     if ($VERBOSE > 2)
+      showcommand 
+     end
+   end
+
+  # locked list
+
+  # 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           minidvodb.active.load
+  host         local
+
+  periods      -poll $LOADPOLL
+  periods      -exec $LOADEXEC
+  periods      -timeout 30
+  npending     1
+
+  stdout NULL
+  stderr $LOGDIR/minidvodb.active.load.log
+
+  task.exec
+    book npages MINIDVODB_ACTIVE -var N
+    if ($N == 0)
+#      echo "No labels for processing"
+      break
+    endif
+
+    book getpage MINIDVODB_ACTIVE 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_ACTIVE $i -var minidvodb_group
+        book setword MINIDVODB_ACTIVE $minidvodb_group STATE NEW
+      end
+      book getpage MINIDVODB_ACTIVE 0 -var minidvodb_group -key STATE NEW
+      
+      # Select different database
+      $MINIDVODB_DB ++
+      if ($MINIDVODB_DB >= $DB:n) set MINIDVODB_DB = 0
+    end
+    book setword MINIDVODB_ACTIVE $minidvodb_group STATE DONE
+    
+    $run = addtool -listminidvodbrun -state new
+    $run = $run -minidvodb_group $minidvodb_group
+    if ($DB:n == 0)
+      option DEFAULT
+    else
+      # save the DB name for the exit tasks
+      option $DB:$MINIDVODB_DB
+      $run = $run -dbname $DB:$MINIDVODB_DB
+      $MINIDVODB_DB ++
+      if ($MINIDVODB_DB >= $DB:n) set MINIDVODB_DB = 0
+    end
+    add_poll_args run
+   
+    command $run
+  end
+
+  # success
+  task.exit    0
+    # convert 'stdout' to book format
+    ipptool2book stdout minidvodbActivelist -key minidvodb_id -uniq -setword dbname $options:0 -setword pantaskState INIT
+    if ($VERBOSE > 2)
+      book listbook minidvodbActivelist
+    end
+    # delete existing entries in the appropriate pantaskStates
+    process_cleanup minidvodbActivelist
+  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           minidvodb.active.run
+  periods      -poll $RUNPOLL
+  periods      -exec $RUNEXEC
+  periods      -timeout 60
+
+  task.exec
+    # if we are unable to run the 'exec', use a long retry time
+    periods -exec $RUNEXEC
+
+    book npages minidvodbActivelist -var N
+    if ($N == 0) break
+   
+    # look for new images in minidvodbActivelist (pantaskState == INIT)
+    book getpage minidvodbActivelist 0 -var pageName -key pantaskState INIT
+    if ("$pageName" == "NULL") break
+
+    book setword minidvodbActivelist $pageName pantaskState RUN
+    book getword minidvodbActivelist $pageName minidvodb_group -var MINIDVODB_GROUP
+    stdout $LOGDIR/minidvodb.active.run.log
+    stderr $LOGDIR/minidvodb.active.run.log
+
+    $run = addtool -flipminidvodbrun -minidvodb_group $MINIDVODB_GROUP
+    
+   
+if ($DB:n == 0)
+      option DEFAULT
+    else
+      # save the DB name for the exit tasks
+      option $DB:$MINIDVODB_DB
+      $run = $run -dbname $DB:$MINIDVODB_DB
+      $MINIDVODB_DB ++
+      if ($MINIDVODB_DB >= $DB:n) set MINIDVODB_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 minidvodbActivelist $options:0 $JOB_STATUS
+  end
+
+  # locked list
+  task.exit    crash
+    #showcommand crash
+    echo "hostname: $JOB_HOSTNAME"
+    # Set a fault code in the database
+    
+    process_exit minidvodbActivelist $options:0 $EXIT_CRASH_ERR
+  end
+
+  # operation timed out?
+    task.exit    timeout
+	showcommand timeout
+    book setword minidvodbActivelist $options:0 pantaskState TIMEOUT
+  end
+end
+
Index: /branches/pap/ippTasks/nightly_stacks.pro
===================================================================
--- /branches/pap/ippTasks/nightly_stacks.pro	(revision 28483)
+++ /branches/pap/ippTasks/nightly_stacks.pro	(revision 28484)
@@ -5,4 +5,6 @@
 macro nightly.stacks.on
     ns.initday.on
+    ns.detrends.off
+    ns.dqstats.on
     ns.registration.on
     ns.burntool.on
@@ -13,4 +15,5 @@
 macro nightly.stacks.off
     ns.initday.off
+    ns.detrends.off
     ns.registration.off
     ns.burntool.off
@@ -21,4 +24,5 @@
 macro ns.on
     ns.initday.on
+    ns.detrends.off
     ns.registration.on
     ns.burntool.on
@@ -29,4 +33,5 @@
 macro ns.off
     ns.initday.off
+    ns.detrends.off
     ns.registration.off
     ns.burntool.off
@@ -41,4 +46,16 @@
 end
 
+macro ns.detrends.on
+  task ns.detrends.load
+    active true
+  end
+end
+
+macro ns.dqstats.on
+  task ns.dqstats.load
+    active true
+  end
+end
+
 macro ns.registration.on
   task ns.registration.load
@@ -76,4 +93,16 @@
 macro ns.initday.off
   task ns.initday.load
+    active false
+  end
+end
+
+macro ns.detrends.off
+  task ns.detrends.load
+    active false
+  end
+end
+
+macro ns.dqstats.off
+  task ns.dqstats.load
     active false
   end
@@ -186,4 +215,75 @@
   #operation times out?
   task.exit    timeout
+    showcommand timeout
+  end
+end
+
+#
+# Queue off any possible detrend verification runs
+#
+task              ns.detrends.load
+  host            local
+  periods         -poll $LOADPOLL
+  periods         -exec $LOADEXEC
+  periods         -timeout 30
+  trange          23:00:00 23:59:59 -nmax 1
+  npending        1
+
+  task.exec
+    stdout $LOGDIR/ns.detrends.log
+    stdout $LOGDIR/ns.detrends.log
+    $today = `date +%Y-%m-%d`
+
+    command automate_stacks.pl --queue_detrends --date $today
+  end
+  
+  task.exit       0
+    # nothign to do here
+  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
+
+#
+# Queue dqstats runs
+#
+task              ns.dqstats.load
+  host            local
+  periods         -poll 3600
+  periods         -exec $LOADEXEC
+  periods         -timeout 30
+  trange          22:00:00 23:59:59
+  trange          00:00:00 10:00:00
+  npending        1
+
+  task.exec
+    stdout $LOGDIR/ns.dqstats.log
+    stderr $LOGDIR/ns.dqstats.log
+    $today = `date +%Y-%m-%d`
+
+    command automate_stacks.pl --queue_dqstats --date $today
+  end
+
+  task.exit       0
+    # nothing to do here
+  end
+  # locked list
+  task.exit       default
+    showcommand failure
+  end
+  task.exit       crash
+    showcommand crash
+  end
+  # operation times out
+  task.exit       timeout
     showcommand timeout
   end
Index: /branches/pap/ippTasks/stack.pro
===================================================================
--- /branches/pap/ippTasks/stack.pro	(revision 28483)
+++ /branches/pap/ippTasks/stack.pro	(revision 28484)
@@ -10,4 +10,5 @@
 ### Initialise the books containing the tasks to do
 book init stackSumSkyfile
+book init stackPendingSummary
 #book init stackCleanup
 
@@ -16,4 +17,5 @@
 $stack_revert_DB = 0
 #$stackCleanup_DB = 0
+$stackSummary_DB = 0
 
 ### Check status of stacking tasks
@@ -26,4 +28,5 @@
 macro stack.reset
   book init stackSumSkyfile
+  book init stackPendingSummary
 #  book init stackCleanup
 end
@@ -40,4 +43,10 @@
     active false
   end
+  task stack.summary.load
+    active true
+  end
+  task stack.summary.run
+    active true
+  end
 end
 
@@ -51,4 +60,10 @@
   end
   task stack.revert
+    active false
+  end
+  task stack.summary.load
+    active false
+  end
+  task stack.summary.run
     active false
   end
@@ -349,4 +364,7 @@
 # end
 
+
+
+
 task stack.revert
   host         local
@@ -400,2 +418,132 @@
 end
 
+### Load tasks for doing the stack summary
+### Tasks are loaded into stackPendingSummary
+task           stack.summary.load
+  host         local
+
+  periods      -poll $LOADPOLL
+  periods      -exec $LOADEXEC
+  periods      -timeout 1200
+  trange       07:00:00 08:00:00 -nmax 1
+  npending     1
+
+  stdout       NULL
+  stderr       $LOGDIR/stack.summary.load
+
+  task.exec
+    if ($LABEL:n == 0) break
+    $run = stacktool -tosummary
+    if ($DB:n == 0) 
+      option DEFAULT
+    else
+      # save the DB name for the exit tasks
+      option $DB:$stackSummary_DB
+      $run = $run -dbname $DB:$stackSummary_DB
+      $stackSummary_DB ++
+      if ($stackSummary_DB >= $DB:n) set stackSummary_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 stackPendingSummary -key sass_id -uniq -setword dbname $options:0 -setword pantaskState INIT
+    if ($VERBOSE > 2)
+      book listbook stackPendingSummary
+    end
+
+    # delete existing entries in the appropriate pantaskStates
+    process_cleanup stackPendingSummary
+  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 doing the stack summary
+task           stack.summary.run
+  periods      -poll $RUNPOLL
+  periods      -exec $RUNEXEC
+  periods      -timeout 60
+
+  task.exec
+    # if we are unable to run the 'exec', use a long retry time
+    periods -exec $RUNEXEC
+
+    book npages stackPendingSummary -var N
+    if ($N == 0) break
+    if ($NETWORK == 0) break
+
+    # look for new images in stackPendingSummary (pantaskState == INIT)
+    book getpage stackPendingSummary 0 -var pageName -key pantaskState INIT
+    if ("$pageName" == "NULL") break
+
+    book setword stackPendingSummary $pageName pantaskState RUN
+    book getword stackPendingSummary $pageName sass_id -var SASS_ID
+    book getword stackPendingSummary $pageName camera -var CAMERA
+    book getword stackPendingSummary $pageName workdir -var WORKDIR_TEMPLATE
+    book getword stackPendingSummary $pageName dbname -var DBNAME
+    book getword stackPendingSummary $pageName tess_id -var TESS_DIR
+    book getword stackPendingSummary $pageName state -var RUN_STATE
+    book getword stackPendingSummary $pageName projection_cell -var projection_cell
+
+    # set the host and workdir
+    host anyhost
+    strsub $WORKDIR_TEMPLATE @HOST@.0 $default_host -var WORKDIR
+
+    basename $TESS_DIR -var TESS_ID
+
+    ## generate outroot specific to this association
+    sprintf outroot "%s/%s/%s.stk.%s.summary" $WORKDIR $TESS_ID $TESS_ID $SASS_ID
+
+    stdout $LOGDIR/stack.summary.log
+    stderr $LOGDIR/stack.summary.log
+
+    $run = skycell_jpeg.pl --stage stack --stage_id $SASS_ID --camera $CAMERA --outroot $outroot
+    add_standard_args run
+
+    # 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 stackPendingSummary $options:0 $JOB_STATUS
+  end
+
+  # locked list
+  task.exit       crash
+    showcommand crash
+    echo "hostname: $JOB_HOSTNAME"
+    book setword stackPendingSummary $options:0 pantaskState CRASH
+  end
+
+  # operation timed out?
+  task.exit       timeout
+    showcommand timeout
+    book setword stackPendingSummary $options:0 pantaskState TIMEOUT
+  end
+end
+
Index: /branches/pap/ippTasks/survey.pro
===================================================================
--- /branches/pap/ippTasks/survey.pro	(revision 28483)
+++ /branches/pap/ippTasks/survey.pro	(revision 28484)
@@ -207,10 +207,11 @@
 
 macro survey.add.addstar
-  if ($0 != 3)
-    echo "USAGE: survey.add.addstar (label) (dvodb)"
+  if ($0 != 4)
+    echo "USAGE: survey.add.addstar (label) (dvodb) (minidvodb_group)"
     break
   end
   book newpage SURVEY_ADDSTAR $1
   book setword SURVEY_ADDSTAR $1 DVODB $2
+  book setword SURVEY_ADDSTAR $1 MINIDVODB_GROUP $3
   book setword SURVEY_ADDSTAR $1 STATE PENDING
 end
@@ -638,4 +639,5 @@
     book setword SURVEY_ADDSTAR $label STATE DONE
     book getword SURVEY_ADDSTAR $label DVODB -var dvodb
+    book getword SURVEY_ADDSTAR $label MINIDVODB_GROUP -var minidvodb_group
   
  #   $run = addtool -definebyquery -destreaked -label $label -set_dvodb $dbodb
@@ -648,5 +650,5 @@
     end
     
-    $run = $run -definebyquery -destreaked -label $label -set_dvodb $dvodb
+    $run = $run -definebyquery -destreaked -label $label -set_dvodb $dvodb -set_minidvodb_group $minidvodb_group -set_minidvodb -set_label $minidvodb_group
     echo $run
     command $run
Index: /branches/pap/ippTasks/warp.pro
===================================================================
--- /branches/pap/ippTasks/warp.pro	(revision 28483)
+++ /branches/pap/ippTasks/warp.pro	(revision 28484)
@@ -13,4 +13,5 @@
 book init warpPendingSkyCell
 book init warpPendingCleanup
+book init warpPendingSummary
 
 ### Database lists
@@ -19,4 +20,5 @@
 $warp_revert_overlap_DB = 0
 $warp_revert_warped_DB = 0
+$warpSummary_DB = 0
 
 ### Check status of warping tasks
@@ -30,4 +32,5 @@
   book init warpInputExp
   book init warpPendingSkyCell
+  book init warpPendingSummary
 end
 
@@ -55,4 +58,10 @@
     active true
   end
+  task warp.summary.load
+    active true
+  end
+  task warp.summary.run
+    active true
+  end
 end
 
@@ -78,4 +87,10 @@
   end
   task warp.revert.warped
+    active false
+  end
+  task warp.summary.load
+    active false
+  end
+  task warp.summary.run
     active false
   end
@@ -525,2 +540,136 @@
   end
 end
+
+### Load tasks for doing the warps
+### Tasks are loaded into warpPendingSkyCell.
+task	       warp.summary.load
+  host         local
+
+  periods      -poll $LOADPOLL
+  periods      -exec $LOADEXEC
+  periods      -timeout 1200
+  npending     1
+
+  stdout NULL
+  stderr $LOGDIR/warp.summary.log
+
+  task.exec
+    if ($LABEL:n == 0) break
+    $run = warptool -tosummary
+    if ($DB:n == 0)
+      option DEFAULT
+    else
+      # save the DB name for the exit tasks
+      option $DB:$warpSummary_DB
+      $run = $run -dbname $DB:$warpSummary_DB
+      $warpSummary_DB ++
+      if ($warpSummary_DB >= $DB:n) set warpSummary_DB = 0
+    end
+    add_poll_args run
+    add_poll_labels run
+    command $run
+  end
+
+  # success
+  task.exit    0
+    # convert 'stdout' to book format
+    # XXX change tess_id to tess_dir when db is updated
+    ipptool2book stdout warpPendingSummary -key warp_id -uniq -setword dbname $options:0 -setword pantaskState INIT
+    if ($VERBOSE > 2)
+      book listbook warpPendingSummary
+    end
+
+    # delete existing entries in the appropriate pantaskStates
+    process_cleanup warpPendingSummary
+  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 calculating the warp overlaps
+### Tasks are taken from warpPendingSkyCell.
+task	       warp.summary.run
+  periods      -poll $RUNPOLL
+  periods      -exec $RUNEXEC
+  periods      -timeout 60
+
+  task.exec
+    # if we are unable to run the 'exec', use a long retry time
+    periods -exec $RUNEXEC
+
+    book npages warpPendingSummary -var N
+    if ($N == 0) break
+    if ($NETWORK == 0) break
+    
+    # look for new images in warpPendingSkyCell (pantaskState == INIT)
+    book getpage warpPendingSummary 0 -var pageName -key pantaskState INIT
+    if ("$pageName" == "NULL") break
+
+    book setword warpPendingSummary $pageName pantaskState RUN
+    book getword warpPendingSummary $pageName warp_id -var WARP_ID
+    book getword warpPendingSummary $pageName camera -var CAMERA
+    book getword warpPendingSummary $pageName workdir -var WORKDIR_TEMPLATE
+    book getword warpPendingSummary $pageName dbname -var DBNAME
+    book getword warpPendingSummary $pageName tess_id -var TESS_DIR
+    book getword warpPendingSummary $pageName exp_tag -var EXP_TAG
+    book getword warpPendingSummary $pageName state -var RUN_STATE
+
+    # set the host and workdir based on the skycell hash
+    host anyhost
+    strsub $WORKDIR_TEMPLATE @HOST@.0 $default_host -var WORKDIR
+#    set.workdir.by.skycell $SKYCELL_ID $WORKDIR_TEMPLATE $default_host WORKDIR
+
+#    if ("$PATH_BASE" == "NULL") 
+        ## generate outroot specific to this exposure
+    basename $TESS_DIR -var TESS_ID
+
+    sprintf outroot "%s/%s/%s.wrp.%s.%s" $WORKDIR $EXP_TAG $EXP_TAG $WARP_ID $TESS_ID
+
+
+    stdout $LOGDIR/warp.summary.log
+    stderr $LOGDIR/warp.summary.log
+
+    $run = skycell_jpeg.pl --stage warp --stage_id $WARP_ID --camera $CAMERA --outroot $outroot
+    add_standard_args run
+
+    # 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 warpPendingSummary $options:0 $JOB_STATUS
+  end
+
+  # locked list
+  task.exit    crash
+    showcommand crash
+    echo "hostname: $JOB_HOSTNAME"
+    book setword warpPendingSummary $options:0 pantaskState CRASH
+  end
+
+  # operation timed out?
+  task.exit    timeout
+    showcommand timeout
+    book setword warpPendingSummary $options:0 pantaskState TIMEOUT
+  end
+end
Index: /branches/pap/ippToPsps/config/init/data.xml
===================================================================
--- /branches/pap/ippToPsps/config/init/data.xml	(revision 28483)
+++ /branches/pap/ippToPsps/config/init/data.xml	(revision 28484)
@@ -101463,5 +101463,61 @@
   <row stackTypeID="9" description="stack type 9" />
   <row stackTypeID="10" description="stack type 10" />
-  </table>
+ </table>
+
+ <table name="ImageFlags">
+   <row name="NEW" value="0" description="no relphot / relastro attempted" />
+   <row name="NOCAL" value="1" description="used within relphot to mean 'don't apply fit'" />
+   <row name="POOR" value="2" description="relphot says image is bad" />
+   <row name="SKIP" value="4" description="external information image is bad" />
+   <row name="FEW" value="8" description="currently too few measurements for good value" />
+ </table>
+
+ <table name="DetectionFlags">
+   <row name="DEFAULT" value="0" description="Initial value: resets all bits" />
+   <row name="PSFMODEL" value="1" description="Source fitted with a psf model (linear or non-linear)" />
+   <row name="EXTMODEL" value="2" description="Source fitted with an extended-source model" />
+   <row name="FITTED" value="4" description="Source fitted with non-linear model (PSF or EXT; good or bad)" />
+   <row name="FITFAIL" value="8" description="Fit (non-linear) failed (non-converge, off-edge, run to zero)" />
+   <row name="POORFIT" value="16" description="Fit succeeds, but low-SN, high-Chisq, or large (for PSF -- drop?)" />
+   <row name="PAIR" value="32" description="Source fitted with a double psf" />
+   <row name="PSFSTAR" value="64" description="Source used to define PSF model" />
+   <row name="SATSTAR" value="128" description="Source model peak is above saturation" />
+   <row name="BLEND" value="256" description="Source is a blend with other sourcers" />
+   <row name="EXTERNALPOS" value="512" description="Source based on supplied input position" />
+   <row name="BADPSF" value="1024" description="Failed to get good estimate of object's PSF" />
+   <row name="DEFECT" value="2048" description="Source is thought to be a defect" />
+   <row name="SATURATED" value="4096" description="Source is thought to be saturated pixels (bleed trail)" />
+   <row name="CR_LIMIT" value="8192" description="Source has crNsigma above limit" />
+   <row name="EXT_LIMIT" value="16384" description="Source has extNsigma above limit" />
+   <row name="MOMENTS_FAILURE" value="32768" description="could not measure the moments" />
+   <row name="SKY_FAILURE" value="65536" description="could not measure the local sky" />
+   <row name="SKYVAR_FAILURE" value="131072" description="could not measure the local sky variance" />
+   <row name="MOMENTS_SN" value="262144" description="moments not measured due to low S/N" />
+   <row name="BIG_RADIUS" value="1048576" description="poor moments for small radius, try large radius" />
+   <row name="AP_MAGS" value="2097152" description="source has an aperture magnitude" />
+   <row name="BLEND_FIT" value="4194304" description="source was fitted as a blend" />
+   <row name="EXTENDED_FIT" value="8388608" description="full extended fit was used" />
+   <row name="EXTENDED_STATS" value="16777216" description="extended aperture stats calculated" />
+   <row name="LINEAR_FIT" value="33554432" description="source fitted with the linear fit" />
+   <row name="NONLINEAR_FIT" value="67108864" description="source fitted with the non-linear fit" />
+   <row name="RADIAL_FLUX" value="134217728" description="radial flux measurements calculated" />
+   <row name="SIZE_SKIPPED" value="268435456" description="size could not be determined" />
+   <row name="ON_SPIKE" value="536870912" description="peak lands on diffraction spike" />
+   <row name="ON_GHOST" value="1073741824" description="peak lands on ghost or glint" />
+   <row name="OFF_CHIP" value="2147483648" description="peak lands off edge of chip" />
+   <row name="NOCAL" value="4294967296" description="detection ignored for this analysis (photcode, time range) -- internal only" />
+   <row name="POOR_PHOTOM" value="8589934592" description="detection is photometry outlier" />
+   <row name="SKIP_PHOTOM" value="17179869184" description="detection was ignored for photometry measurement" />
+   <row name="MEAS_AREA" value="34359738368" description="detection near image edge" />
+   <row name="POOR_ASTROM" value="68719476736" description="detection is astrometry outlier" />
+   <row name="SKIP_ASTROM" value="137438953472" description="detection was ignored for astrometry measurement" />
+   <row name="USED_OBJ" value="274877906944" description="detection was used during opdate objects" />
+   <row name="USED_CHIP" value="549755813888" description="detection was used during update chips" />
+   <row name="BLEND_MEAS" value="1099511627776" description="detection is within radius of multiple objects" />
+   <row name="BLEND_OBJ" value="2199023255552" description="multiple detections within radius of object" />
+   <row name="BLEND_MEAS_X" value="17592186044416" description="detection is within radius of multiple objects across catalogs" />
+   <row name="ARTIFACT" value="35184372088832" description="detection is thought to be non-astronomical" />
+ </table>
+
 
   <table name="PhotoCal">
Index: /branches/pap/ippToPsps/config/init/tables.xml
===================================================================
--- /branches/pap/ippToPsps/config/init/tables.xml	(revision 28483)
+++ /branches/pap/ippToPsps/config/init/tables.xml	(revision 28484)
@@ -112,3 +112,13 @@
     <column name="description" type="TSTRING" default=" " comment="survey description"></column>
   </table>
+  <table name="ImageFlags">
+    <column name="name" type="TSTRING" default=" " comment="Name of flag"></column>
+    <column name="value" type="TLONGLONG" default="0" comment="Flag value"></column>
+    <column name="description" type="TSTRING" default=" " comment="Description of the flag"></column>
+  </table>
+  <table name="DetectionFlags">
+    <column name="name" type="TSTRING" default=" " comment="Name of flag"></column>
+    <column name="value" type="TLONGLONG" default="0" comment="Flag value"></column>
+    <column name="description" type="TSTRING" default=" " comment="Description of the flag"></column>
+  </table>
 </tableDescriptions>
Index: /branches/pap/ippToPsps/scripts/createDb.pl
===================================================================
--- /branches/pap/ippToPsps/scripts/createDb.pl	(revision 28484)
+++ /branches/pap/ippToPsps/scripts/createDb.pl	(revision 28484)
@@ -0,0 +1,105 @@
+#!/usr/bin/env perl
+
+use warnings;
+use strict;
+
+use DBI;
+use constant DB_SOCKET => '/var/run/mysqld/mysqld.sock';
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+
+my $dbname = 'ippToPsps';
+my $dbserver = 'ippdb01';
+my $dbuser = 'ipp';
+my $dbpass = 'ipp';
+GetOptions( 'dbname|d=s' => \$dbname);
+
+# connect to database
+my $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";
+
+print "\n*******************************************************************************\n*\n";
+
+print "* Connected to '$dbname' on 'dbserver'\n";
+
+if (!doesTableExist("revision")) {
+    createRevision_1();
+}
+
+
+$db->disconnect();
+print "* Disconnected from '$dbname' on 'dbserver'\n";
+print "*\n*******************************************************************************\n\n";
+
+#######################################################################################
+# 
+# Create revision 1 of the database 
+#
+#######################################################################################
+sub createRevision_1 {
+
+    print "* Creating revision 1 of '$dbname'\n";
+
+    my $query = $db->prepare(<<SQL);
+    CREATE TABLE revision (
+
+            revision INT, 
+            created TIMESTAMP DEFAULT NOW(),
+            primary key (revision)
+            );
+SQL
+        $query->execute;
+
+    $query = $db->prepare(<<SQL);
+    INSERT INTO revision (revision) VALUES (1);
+SQL
+        $query->execute;
+
+    $query = $db->prepare(<<SQL);
+
+    CREATE TABLE batches (
+
+            batch_id BIGINT NOT NULL, 
+            exp_id BIGINT NOT NULL, 
+            survey_id VARCHAR(30) DEFAULT "NONE",
+            processed TINYINT DEFAULT 0,
+            on_datastore TINYINT DEFAULT 0,
+            loaded_to_ODM TINYINT DEFAULT 0,
+            merge_worthy TINYINT DEFAULT 0,
+            deleted TINYINT DEFAULT 0,
+            created TIMESTAMP DEFAULT NOW(),
+            primary key (batch_id, exp_id)
+            );
+
+SQL
+    $query->execute;
+
+}
+
+#######################################################################################
+# 
+# Gets current revision of ippToPsps database
+#
+#######################################################################################
+sub doesTableExist {
+    my ($table) = @_;
+
+    my $query = $db->prepare(<<SQL);
+
+    SELECT COUNT(*) 
+        FROM information_schema.tables 
+        WHERE table_schema = '$dbname'  
+        AND table_name = '$table';
+SQL
+
+        $query->execute;
+
+    my $count = $query->fetchrow_array();
+
+    printf( "* Table '$table' %s\n", $count ? "exists" : "does not exist");
+
+    return $count;
+}
+
Index: /branches/pap/ippToPsps/scripts/ippToPsps_run.pl
===================================================================
--- /branches/pap/ippToPsps/scripts/ippToPsps_run.pl	(revision 28483)
+++ /branches/pap/ippToPsps/scripts/ippToPsps_run.pl	(revision 28484)
@@ -12,5 +12,5 @@
 
 # globals
-my ($verbose, $save_temps, $no_op, $no_update, $camera, $batchType, $output, $dvodb, $datastoreProduct, $label, $singleExpId, $no_publish, $force);
+my ($verbose, $save_temps, $no_op, $no_update, $camera, $batchType, $output, $dvodb, $datastoreProduct, $survey, $singleExpId, $no_publish, $force, $initBatch);
 
 # default values for certain globals
@@ -25,4 +25,5 @@
 $datastoreProduct = "PSPS_test";
 $force = 0;
+$initBatch = 0;
 
 # get user args
@@ -31,5 +32,5 @@
         'batch|b=s' => \$batchType,
         'dvodb|d=s' => \$dvodb,
-        'label|l=s' => \$label,
+        'survey|s=s' => \$survey,
         'expid|e=s' => \$singleExpId,
         'product|p=s' => \$datastoreProduct,
@@ -49,5 +50,5 @@
         "--batch <init|det|diff|stack>\n".
         "--output <path>\n".
-        "--label <label> or --expid <expid>\n" .
+        "--survey <ThreePi|MD01,2 etc|STS|SAS|SweetSpot> or --expid <expid>\n" .
         "--dvodb <path>\n".
         "\n   Optional:\n\n".
@@ -62,5 +63,7 @@
 defined $output and
 defined $dvodb and
-( defined $label or defined $singleExpId );
+(( defined $survey or defined $singleExpId ) or ( $batchType eq "init"));
+
+if ($batchType eq "init") {$initBatch = 1;}
 
 # check we can run programs and get camera config
@@ -70,5 +73,5 @@
 
 # make a temporary file for saving results
-my ($resultsFile, $resultsFileName) = tempfile( "/tmp/results.XXXX", UNLINK => !$save_temps);
+my ($resultsFile, $resultsFileName) = tempfile( "/tmp/ippToPsps_results.XXXX", UNLINK => !$save_temps);
 
 process();
@@ -86,5 +89,5 @@
     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 "STS";}
+    if ($distGroup =~ m/^sts$/i) {return "STS1";}
     if ($distGroup =~ m/^SweetSpot$/i) {return "SSS";}
     if ($distGroup =~ m/^(3PI)|(ThreePi)|(SAS)$/i) {return "3PI";}
@@ -118,5 +121,5 @@
 #######################################################################################
 # 
-# Finds all exposures for the provided label and generates PSPS FITS data for them
+# Finds all exposures for the provided TODO and generates PSPS FITS data for them
 #
 #######################################################################################
@@ -130,17 +133,36 @@
 
     my $query;
-    if (!$singleExpId) {
-
-        # query to retrieve all exposures with the provided label
+    if ($initBatch) {
+    
         $query = $gpc1Db->prepare(<<SQL);
 
-        SELECT DISTINCT rawExp.exp_id, camRun.dist_group
-            FROM camRun, chipRun, rawExp
-            WHERE camRun.chip_id = chipRun.chip_id
-            AND chipRun.exp_id = rawExp.exp_id
-            AND camRun.dist_group LIKE 'ThreePi'
-            AND rawExp.exp_id > $lastExpId
-            ORDER BY rawExp.exp_id ASC
+        SELECT 0, 'NULL', 'ThreePi';
 SQL
+    }
+    elsif ($singleExpId) {
+
+        $query = $gpc1Db->prepare(<<SQL);
+
+        SELECT DISTINCT rawExp.exp_id,  rawExp.exp_name, dist_group 
+            FROM rawExp, chipRun 
+            WHERE chipRun.exp_id = rawExp.exp_id 
+            AND rawExp.exp_id = $singleExpId
+SQL
+    }
+    else {
+
+        $query = $gpc1Db->prepare(<<SQL);
+
+        SELECT rawExp.exp_id, rawExp.exp_name, camRun.dist_group 
+            FROM camRun, chipRun, rawExp 
+            WHERE camRun.state = 'full' 
+            AND camRun.chip_id = chipRun.chip_id 
+            AND chipRun.exp_id = rawExp.exp_id 
+            AND camRun.dist_group = '$survey'   
+            GROUP BY  rawExp.exp_id 
+            ORDER BY rawExp.exp_id ASC;
+SQL
+
+            #AND rawExp.dateobs <= '2010-03-12'
             #AND rawExp.exp_id > $lastExpId
             #AND camRun.label LIKE '%$label%'
@@ -149,11 +171,4 @@
             #AND rawExp.decl >= '-0.157079633' AND rawExp.decl <= '0.157079633' Jims Dec range
     }
-    else {
-
-        $query = $gpc1Db->prepare(<<SQL);
-
-        SELECT DISTINCT rawExp.exp_id, dist_group FROM rawExp, chipRun WHERE chipRun.exp_id = rawExp.exp_id AND rawExp.exp_id = $singleExpId
-SQL
-    }
 
     my $batchId = 0;
@@ -168,11 +183,11 @@
     # loop round exposures
     while (my @row = $query->fetchrow_array()) {
-        my ($expId, $distGroup) = @row;
-
-        if (isExposureAlreadyProcessed($ippToPspsDb, $expId)) {
+        my ($expId, $expName, $distGroup) = @row;
+
+        if (!$initBatch && isExposureAlreadyProcessed($ippToPspsDb, $expId)) {
                 
                 if ($force) {print "* Forcing....\n";} 
                 else {next};
-                }
+        }
 
         my $surveyType = getSurveyTypeFromDistGroup($distGroup);
@@ -181,5 +196,5 @@
         $jobId = getNewBatchId($ippToPspsDb, $expId, $surveyType);
 
-# TODO quit here if no sensible job ID
+        # TODO quit here if no sensible job ID
 
         # generate batch and job paths from job and batch IDs
@@ -188,13 +203,11 @@
         my $batchDir = sprintf("B%03d", $batchId);
         my $batchOutputPath = sprintf("$jobOutputPath/%s", $batchDir);
-
-        print "* Preparing exposure $expId as job '$job'...\n";
-
-        # make directories
         mkdir($jobOutputPath, 0777);
         mkdir($batchOutputPath, 0777);
 
+        print "* Preparing exposure $expId as job '$job'...\n";
+
         # run IppToPsps program
-        if (runIppToPsps($gpc1Db, $expId, $batchOutputPath, $batchType)) {
+        if (runIppToPsps($gpc1Db, $expId, $expName, $surveyType, $batchOutputPath, $batchType)) {
 
             # write batch manifest and tar and zip up
@@ -232,6 +245,5 @@
         }
 
-        if ($batchType eq "init" ) {print "* Wrote initialisation batch\n"; last;}
-
+        if ($initBatch) {print "* Wrote initialisation batch\n";}
 
         print "*\n*******************************************************************************\n\n";
@@ -358,17 +370,16 @@
 #######################################################################################
 sub runIppToPsps {
-    my ($gpc1Db, $expId, $batchOutputPath, $batchType) = @_;
+    my ($gpc1Db, $expId, $expName, $surveyType, $batchOutputPath, $batchType) = @_;
 
     my $success = 0;
 
-    # before anything else, we can publish the init batch
     if ($batchType =~ /init/) {
         $success = produceInit($batchOutputPath);
     }
     elsif ($batchType =~ /det/) {
-        $success = produceDetections($gpc1Db, $expId, $batchOutputPath);
+        $success = produceDetections($gpc1Db, $expId, $expName, $surveyType, $batchOutputPath);
     }
     elsif ($batchType =~ /diff/) {
-        $success = produceDiffs($gpc1Db, $expId, $batchOutputPath);
+        $success = produceDiffs($gpc1Db, $expId, $expName, $surveyType, $batchOutputPath);
     }
 
@@ -614,5 +625,5 @@
     my $fullJobPath = "$path/$job";
 
-    my ($tempFile, $tempName) = tempfile( "/tmp/dsregList.XXXX", UNLINK => !$save_temps);
+    my ($tempFile, $tempName) = tempfile( "/tmp/ippToPsps_dsregList.XXXX", UNLINK => !$save_temps);
 
     # loop through all batch files in this job directory
@@ -664,5 +675,5 @@
     my ($outputPath) = @_;
 
-    my $success = runProgram( "NULL", $outputPath, 0, $batchType);
+    my $success = runProgram("NULL", $outputPath, 0, "NULL", "NULL", $batchType);
     return $success;
 }
@@ -673,5 +684,5 @@
 #######################################################################################
 sub produceDetections {
-    my ($gpc1Db,$expId,$outputPath) = @_;
+    my ($gpc1Db, $expId, $expName, $surveyType, $outputPath) = @_;
 
     # query to retrieve nebulous key of camera smf file for this exposure
@@ -705,9 +716,9 @@
 
         # now write the path to this file out to temp file
-        my ($tempFile, $tempName) = tempfile( "/tmp/inputFileList.XXXX", UNLINK => !$save_temps);
+        my ($tempFile, $tempName) = tempfile( "/tmp/ippToPsps_inputFileList.XXXX", UNLINK => !$save_temps);
         print $tempFile $realFile . "\n";
         close $tempFile;
 
-        $success = runProgram($tempName, $outputPath, $expId, $batchType);
+        $success = runProgram($tempName, $outputPath, $expId, $expName, $surveyType, $batchType);
         return $success;
     }
@@ -720,5 +731,5 @@
 #######################################################################################
 sub produceDiffs {
-    my ($gpc1Db,$expId,$outputPath) = @_;
+    my ($gpc1Db, $expId, $expName, $surveyType, $outputPath) = @_;
 
     my $query = 
@@ -758,5 +769,5 @@
     }
 
-    my $ret = runProgram($tempName, $outputPath, $expId, $batchType);
+    my $ret = runProgram($tempName, $outputPath, $expId, $expName, $surveyType, $batchType);
 
     close $tempFile;
@@ -771,5 +782,5 @@
 #######################################################################################
 sub runProgram {
-    my ($input, $output, $expid, $batchType) = @_;
+    my ($input, $output, $expid, $expName, $surveyType, $batchType) = @_;
 
     # build command
@@ -780,4 +791,6 @@
     $command .= " -config config"; # TODO
     $command .= " -expid $expid";
+    $command .= " -expname $expName";
+    $command .= " -survey $surveyType";
     $command .= " -batch $batchType";
     $command .= " -results $resultsFileName";
Index: /branches/pap/ippToPsps/scripts/pspsSchema2xml.pl
===================================================================
--- /branches/pap/ippToPsps/scripts/pspsSchema2xml.pl	(revision 28483)
+++ /branches/pap/ippToPsps/scripts/pspsSchema2xml.pl	(revision 28484)
@@ -122,4 +122,6 @@
     parseTable("Region");
     parseTable("StackType");
+    parseTable("ImageFlags");
+    parseTable("DetectionFlags");
 }
 
Index: /branches/pap/ippToPsps/src/ippToPsps.c
===================================================================
--- /branches/pap/ippToPsps/src/ippToPsps.c	(revision 28483)
+++ /branches/pap/ippToPsps/src/ippToPsps.c	(revision 28484)
@@ -93,5 +93,4 @@
     if (this->numOfInputFiles < 1) return false;
 
-
     this->inputFiles = (char**)calloc(this->numOfInputFiles, sizeof(char*));
     for(uint32_t i=0; i<this->numOfInputFiles; i++)
@@ -144,4 +143,6 @@
     psMetadata *arguments = psMetadataAlloc();
     psMetadataAddStr(arguments, PS_LIST_TAIL, "-expid", 0, "Exposure ID", NULL);
+    psMetadataAddStr(arguments, PS_LIST_TAIL, "-expname", 0, "Exposure name", NULL);
+    psMetadataAddStr(arguments, PS_LIST_TAIL, "-survey", 0, "Survey type", NULL);
     psMetadataAddStr(arguments, PS_LIST_TAIL, "-input", 0, "Path to FITS inout", NULL);
     psMetadataAddStr(arguments, PS_LIST_TAIL, "-output", 0, "Path to FITS output", NULL);
@@ -161,4 +162,6 @@
         if (tmp) this->expId = atoi(tmp);
         //free(tmp); tmp = NULL;
+        this->expName = psMemIncrRefCounter(psMetadataLookupStr(NULL, arguments, "-expname"));
+        this->surveyType = psMemIncrRefCounter(psMetadataLookupStr(NULL, arguments, "-survey"));
         this->fitsInPath = psMemIncrRefCounter(psMetadataLookupStr(NULL, arguments, "-input"));
         this->resultsPath = psMemIncrRefCounter(psMetadataLookupStr(NULL, arguments, "-results"));
@@ -176,4 +179,6 @@
 
         if (
+                (this->batchType != BATCH_INIT && !this->expName) || 
+                (this->batchType != BATCH_INIT && !this->surveyType) || 
                 (this->batchType != BATCH_INIT && !this->fitsInPath) || 
                 (this->batchType != BATCH_INIT && !this->resultsPath) || 
@@ -237,4 +242,6 @@
 
     this->expId = -1;
+    this->expName = NULL;
+    this->surveyType = NULL;
     this->fitsInPath = NULL;
     this->resultsPath = NULL;
Index: /branches/pap/ippToPsps/src/ippToPsps.h
===================================================================
--- /branches/pap/ippToPsps/src/ippToPsps.h	(revision 28483)
+++ /branches/pap/ippToPsps/src/ippToPsps.h	(revision 28484)
@@ -21,4 +21,6 @@
 
     uint32_t expId;            // the exposure ID to be used
+    psString expName;          // the exposure name
+    psString surveyType;       // the survey type, eg 3PI, MD01, STS, SSS
     uint8_t batchType;         // PSPS batch type
     psString fitsInPath;       // path to FITS input
Index: /branches/pap/ippToPsps/src/ippToPspsBatchDetection.c
===================================================================
--- /branches/pap/ippToPsps/src/ippToPspsBatchDetection.c	(revision 28483)
+++ /branches/pap/ippToPsps/src/ippToPspsBatchDetection.c	(revision 28484)
@@ -13,14 +13,8 @@
 #include "fitsio.h"
 
-// Gets flux from magnitude
+// Gets uncalibrated instrumental flux from magnitude
 static __inline bool ippToPsps_getFlux(const float zeroPoint, const float exposureTime, const float magnitude, float* flux) {
 
-    // use uncalibrated instrumental flux
     *flux = powf(10.0, -0.4*magnitude) / exposureTime;
-
-    // use calibrated flux in Janskys, where 3631 Jy is the zero point flux for the filter: constant over all filters because we're using AB magnitudes
-    //float flux = 3631 * powf(10.0, -0.4*(magnitude + zeroPoint)) / exposureTime;
-
-    //    printf("mag=%f\texpTime=%f\tzeroPoint=%f\tflux=%f\n", magnitude, exposureTime, zeroPoint, flux);
     return (!isfinite(*flux) || *flux < 0.000001) ? false : true;
 }
@@ -54,19 +48,27 @@
     float zptObs, zeroPoint, exposureTime;
     char filterType[20];
+    double obsTime;
+    double expStart;
+    double expTime;
     status=0; fits_read_key(fitsIn, TFLOAT, "ZPT_OBS", &zptObs, NULL, &status);
     status=0; fits_read_key(fitsIn, TFLOAT, "ZPT_REF", &zeroPoint, NULL, &status);
     status=0; fits_read_key(fitsIn, TFLOAT, "EXPREQ", &exposureTime, NULL, &status);
     status=0; fits_read_key(fitsIn, TSTRING, "FILTERID", filterType, NULL, &status);
-    
+    status=0; fits_read_key(fitsIn, TDOUBLE, "MJD-OBS", &expStart, NULL, &status);
+    status=0; fits_read_key(fitsIn, TDOUBLE, "EXPTIME", &expTime, NULL, &status);
+    obsTime = expStart + (expTime/172800); // exp start plus half exp time (converted from secs to days)
+   
     ippToPspsConfig_writeTable(this->config, fitsIn, this->fitsOut, 1, "FrameMeta", true);
 
     // FrameMeta values
     fits_write_col(this->fitsOut, TLONG, FRAMEMETA_FRAMEID, 1, 1, 1, &this->expId, &status);
-
-    int8_t surveyID = 0; // TODO
+    fits_write_col(this->fitsOut, TSTRING, FRAMEMETA_FRAMENAME, 1, 1, 1, &this->expName, &status);
+
+    int8_t surveyID = -1;
+    if (!ippToPspsConfig_getSurveyId(this->config, this->surveyType, &surveyID)) {return PS_EXIT_DATA_ERROR;}
     fits_write_col(this->fitsOut, TBYTE, FRAMEMETA_SURVEYID, 1, 1, 1, &surveyID, &status);
 
     int8_t filterID = -1;
-    ippToPspsConfig_getFilterId(this->config, filterType, &filterID);
+    if (!ippToPspsConfig_getFilterId(this->config, filterType, &filterID)) {return PS_EXIT_DATA_ERROR;}
     fits_write_col(this->fitsOut, TBYTE, FRAMEMETA_FILTERID, 1, 1, 1, &filterID, &status);
 
@@ -81,5 +83,4 @@
 
     // stuff to keep from psf.hdr header
-    double obsTime = 0.0;
     int sourceId = -1;
     int imageId = -1;
@@ -97,5 +98,5 @@
     int maxDvoDetId = -1;
     int numDvoDetections = -1;
-    Image *pImage = NULL;
+    Image *image = NULL;
 
     // stuff for detections table
@@ -130,5 +131,5 @@
     long ippDetectID[MAXDETECT];
     long imageID[MAXDETECT];
-    float obsTimes[MAXDETECT];
+    double obsTimes[MAXDETECT];
     float instFlux[MAXDETECT];
     float instFluxErr[MAXDETECT];
@@ -150,4 +151,5 @@
     long maxObjID = LONG_MIN; 
     long minObjID = LONG_MAX;
+    uint64_t imageFlags;
     short nOta = 0;
     long i;
@@ -156,4 +158,5 @@
     uint32_t numInvalidFlux;
     long numDetectionsOut;
+    long totalDetectionsOut = 0;
     long removeList[MAXDETECT];
     long thisObjId;
@@ -193,20 +196,21 @@
             status=0; fits_read_key(fitsIn, TFLOAT, "IQ_FW2", &momentMin, NULL, &status);
             status=0; fits_read_key(fitsIn, TLONG, "IMAGEID", &imageId, NULL, &status);
-            status=0; fits_read_key(fitsIn, TDOUBLE, "MJD-OBS", &obsTime, NULL, &status);
             status=0; fits_read_key(fitsIn, TLONG, "SOURCEID", &sourceId, NULL, &status);
 
             // access DVO database
-            skylist = dvoSkyListByExternID(this->dvoConfig, sourceId, imageId, &pImage);
+            skylist = dvoSkyListByExternID(this->dvoConfig, sourceId, imageId, &image);
             if (skylist == NULL) {
-                psError(PS_ERR_IO, false, "DVO: can't find SkyList for sourceId='%d' imageId='%d' (CCD = XY%s): skipping\n", sourceId, imageId, ccdNumber);
+                psError(PS_ERR_IO, false, 
+                        "DVO: can't find SkyList for sourceId='%d' imageId='%d' (CCD = XY%s): skipping\n", 
+                        sourceId, imageId, ccdNumber);
                 continue;
             }
 
             // create unique int from 'frameID' (aka exposure ID) and ccd number
-            pspsImageId = (this->expId*100) + pImage->ccdnum;
+            pspsImageId = (this->expId*100) + image->ccdnum;
 
             // now get DVO detections
             dvoDetections = NULL;
-            numDvoDetections = dvoGetDetections(skylist, pImage->imageID, &dvoDetections, &maxDvoDetId);
+            numDvoDetections = dvoGetDetections(skylist, image->imageID, &dvoDetections, &maxDvoDetId);
 
             // TODO check nDet < MAXDETECT
@@ -216,8 +220,9 @@
             psfFwhm = (fwhmMaj+fwhmMin)/2;
             momentFwhm = (momentMaj+momentMin)/2;
+            imageFlags = (uint64_t)image->flags;
             fits_write_col(this->fitsOut, TLONGLONG, IMAGEMETA_IMAGEID, 1, 1, 1, &pspsImageId, &status);
             fits_write_col(this->fitsOut, TLONG, IMAGEMETA_FRAMEID, 1, 1, 1, &this->expId, &status);
-            fits_write_col(this->fitsOut, TSHORT, IMAGEMETA_CCDID, 1, 1, 1, &pImage->ccdnum, &status);
-            fits_write_col(this->fitsOut, TLONG, IMAGEMETA_PHOTOCALID, 1, 1, 1, &pImage->photcode, &status);
+            fits_write_col(this->fitsOut, TSHORT, IMAGEMETA_CCDID, 1, 1, 1, &image->ccdnum, &status);
+            fits_write_col(this->fitsOut, TLONG, IMAGEMETA_PHOTOCALID, 1, 1, 1, &image->photcode, &status);
             fits_write_col(this->fitsOut, TBYTE, IMAGEMETA_FILTERID, 1, 1, 1, &filterID, &status);
             fits_write_col(this->fitsOut, TFLOAT, IMAGEMETA_PHOTOSCAT, 1, 1, 1, &zptObs, &status);
@@ -225,4 +230,5 @@
             fits_write_col(this->fitsOut, TFLOAT, IMAGEMETA_MOMENTFWHM, 1, 1, 1, &momentFwhm, &status);
             fits_write_col(this->fitsOut, TFLOAT, IMAGEMETA_PHOTOZERO, 1, 1, 1, &zptObs, &status);
+            fits_write_col(this->fitsOut, TLONGLONG, IMAGEMETA_QAFLAGS, 1, 1, 1, &imageFlags, &status);
 
             // now move BACK to detections table in smf
@@ -329,5 +335,5 @@
             fits_write_col(this->fitsOut, TBYTE, DETECTION_SURVEYID, 1, 1, nDet, surveyIDs, &status);
             fits_write_col(this->fitsOut, TLONGLONG, DETECTION_IMAGEID, 1, 1, nDet, imageID, &status);
-            fits_write_col(this->fitsOut, TFLOAT, DETECTION_OBSTIME, 1, 1, nDet, obsTimes, &status);
+            fits_write_col(this->fitsOut, TDOUBLE, DETECTION_OBSTIME, 1, 1, nDet, obsTimes, &status);
             fits_write_col(this->fitsOut, TFLOAT, DETECTION_INSTFLUX, 1, 1, nDet, instFlux, &status);
             fits_write_col(this->fitsOut, TFLOAT, DETECTION_INSTFLUXERR, 1, 1, nDet, instFluxErr, &status);
@@ -357,8 +363,12 @@
                     extensionName, nDet, numDetectionsOut, unmatched, numOfDuplicates, numInvalidFlux);
 
+            totalDetectionsOut += numDetectionsOut;
+
             if (dvoDetections!= NULL) dvoFree(dvoDetections);
             SkyListFree(skylist);
         }
     }
+
+    psLogMsg("ippToPsps", PS_LOG_INFO, "Total detections for this exposure = %ld\n", totalDetectionsOut);
 
     for (uint32_t i=0; i<MAXDETECT;i++) free(assocDate[i]);
@@ -382,4 +392,6 @@
         if (fprintf(this->resultsFile, "%ld\n", maxObjID) < 1 ) 
             psError(PS_ERR_IO, false, "Unable to write max Object ID to'%s'\n", this->resultsPath);
+        if (fprintf(this->resultsFile, "%ld\n", totalDetectionsOut) < 1 ) 
+            psError(PS_ERR_IO, false, "Unable to write totalDetectionsOut to'%s'\n", this->resultsPath);
     }
 
Index: /branches/pap/ippToPsps/src/ippToPspsConfig.c
===================================================================
--- /branches/pap/ippToPsps/src/ippToPspsConfig.c	(revision 28483)
+++ /branches/pap/ippToPsps/src/ippToPspsConfig.c	(revision 28484)
@@ -325,4 +325,18 @@
 
     return ret;
+}
+
+// gets survey ID from survey name
+bool ippToPspsConfig_getSurveyId(IppToPspsConfig* this, const char* surveyType, int8_t* surveyId) {
+
+    char buffer[10];
+    if (!ippToPspsConfig_getInitValue(this, "Survey", "name", surveyType, "surveyID", buffer)) {
+
+        *surveyId = -1;
+        return false;
+    }
+
+    *surveyId = atoi(buffer);
+    return true;
 }
 
Index: /branches/pap/ippToPsps/src/ippToPspsConfig.h
===================================================================
--- /branches/pap/ippToPsps/src/ippToPspsConfig.h	(revision 28483)
+++ /branches/pap/ippToPsps/src/ippToPspsConfig.h	(revision 28484)
@@ -59,4 +59,5 @@
 // getters
 bool ippToPspsConfig_getFilterId(IppToPspsConfig* this, const char* filterType, int8_t* filterId);
+bool ippToPspsConfig_getSurveyId(IppToPspsConfig* this, const char* surveyType, int8_t* surveyId);
 
 #endif // IPPTOPSPSCONFIG_H
Index: /branches/pap/ippTools/share/Makefile.am
===================================================================
--- /branches/pap/ippTools/share/Makefile.am	(revision 28483)
+++ /branches/pap/ippTools/share/Makefile.am	(revision 28484)
@@ -4,335 +4,353 @@
 
 dist_pkgdata_DATA = \
-     addtool_donecleanup.sql \
-     addtool_find_cam_id.sql \
-     addtool_find_pendingexp.sql \
-     addtool_find_processedexp.sql \
-     addtool_pendingcleanupexp.sql \
-     addtool_pendingcleanuprun.sql \
-     addtool_queue_cam_id.sql \
-     addtool_reset_faulted_runs.sql \
-     addtool_revertprocessedexp.sql \
-     bgtool_advancechip.sql \
-     bgtool_advancewarp.sql \
-     bgtool_chipinputs.sql \
-     bgtool_chip.sql \
-     bgtool_cleanedchip.sql \
-     bgtool_cleanedwarp.sql \
-     bgtool_definechip.sql \
-     bgtool_definewarp.sql \
-     bgtool_revertchip.sql \
-     bgtool_revertwarp.sql \
-     bgtool_tochip.sql \
-     bgtool_tocleanchip.sql \
-     bgtool_tocleanwarp.sql \
-     bgtool_towarp.sql \
-     bgtool_warpinputs.sql \
-     bgtool_warp.sql \
-     camtool_donecleanup.sql \
-     camtool_find_chip_id.sql \
-     camtool_find_pendingexp.sql \
-     camtool_find_pendingimfile.sql \
-     camtool_find_processedexp.sql \
-     camtool_pendingcleanupexp.sql \
-     camtool_pendingcleanuprun.sql \
-     camtool_queue_chip_id.sql \
-     camtool_reset_faulted_runs.sql \
-     camtool_revertprocessedexp.sql \
-     camtool_export_run.sql \
-     camtool_export_processed_exp.sql \
-     chiptool_change_exp_state.sql \
-     chiptool_change_imfile_data_state.sql \
-     chiptool_completely_processed_exp.sql \
-     chiptool_coalesce_run.sql \
-     chiptool_definecopy.sql \
-     chiptool_donecleanup.sql \
-     chiptool_find_rawexp.sql \
-     chiptool_listrun.sql \
-     chiptool_pendingcleanupimfile.sql \
-     chiptool_pendingcleanuprun.sql \
-     chiptool_pendingimfile.sql \
-     chiptool_processedimfile.sql \
-     chiptool_revertprocessedimfile.sql \
-     chiptool_revertupdatedimfile.sql \
-     chiptool_run.sql \
-     chiptool_runstate.sql \
-     chiptool_setimfiletoupdate.sql \
-     chiptool_export_run.sql \
-     chiptool_export_imfile.sql \
-     chiptool_export_processed_imfile.sql \
-     chiptool_unmasked.sql \
-     detselect_search.sql \
-     detselect_select.sql \
-     dettool_addprocessedexp.sql \
-     dettool_childlessrun.sql \
-     dettool_definebydetrun.sql \
-     dettool_detrunsummary.sql \
-     dettool_find_completed_runs.sql \
-     dettool_input.sql \
-     dettool_normalizedexp.sql \
-     dettool_normalizedimfile.sql \
-     dettool_normalizedstat.sql \
-     dettool_pending.sql \
-     dettool_pendingcleanup_detrunsummary.sql \
-     dettool_pendingcleanup_normalizedexp.sql \
-     dettool_pendingcleanup_normalizedimfile.sql \
-     dettool_pendingcleanup_normalizedstat.sql \
-     dettool_pendingcleanup_processedexp.sql \
-     dettool_pendingcleanup_processedimfile.sql \
-     dettool_pendingcleanup_residexp.sql \
-     dettool_pendingcleanup_residimfile.sql \
-     dettool_pendingcleanup_stacked.sql \
-     dettool_processedimfile.sql \
-     dettool_raw.sql \
-     dettool_residexp.sql \
-     dettool_residimfile.sql \
-     dettool_revertdetrunsummary.sql \
-     dettool_revertnormalizedexp.sql \
-     dettool_revertnormalizedimfile.sql \
-     dettool_revertnormalizedstat.sql \
-     dettool_revertprocessedexp.sql \
-     dettool_revertprocessedimfile.sql \
-     dettool_revertresidexp.sql \
-     dettool_revertresidimfile.sql \
-     dettool_revertstacked.sql \
-     dettool_runs.sql \
-     dettool_stacked.sql \
-     dettool_start_new_iteration.sql \
-     dettool_stop_completed_correct_runs.sql \
-     dettool_tocorrectexp.sql \
-     dettool_tocorrectimfile.sql \
-     dettool_todetrunsummary.sql \
-     dettool_tonormalize.sql \
-     dettool_tonormalizedexp.sql \
-     dettool_tonormalizedstat.sql \
-     dettool_toprocessedexp.sql \
-     dettool_toprocessedimfile.sql \
-     dettool_toresidexp.sql \
-     dettool_toresidimfile.sql \
-     dettool_tostacked.sql \
-     difftool_change_skyfile_data_state.sql \
-     difftool_change_run_state.sql \
-     difftool_completed_runs.sql \
-     difftool_coalesce_run.sql \
-     difftool_definewarpstack_part1.sql \
-     difftool_definewarpstack_part2.sql \
-     difftool_definewarpstack_temp_create.sql \
-     difftool_definewarpwarp_temp_create.sql \
-     difftool_definewarpwarp_temp_insert.sql \
-     difftool_definewarpwarp_select.sql \
-     difftool_definewarpwarp_insert.sql \
-     difftool_definestackstack_part0.sql \
-     difftool_definestackstack_part1.sql \
-     difftool_donecleanup.sql \
-     difftool_export_input_skyfile.sql \
-     difftool_export_run.sql \
-     difftool_export_skyfile.sql \
-     difftool_inputskyfile.sql \
-     difftool_listrun.sql \
-     difftool_pendingcleanuprun.sql \
-     difftool_pendingcleanupskyfile.sql \
-     difftool_revertdiffskyfile_delete.sql \
-     difftool_revertdiffskyfile_updated.sql \
-     difftool_setskyfiletoupdate.sql \
-     difftool_skyfile.sql \
-     difftool_todiffskyfile.sql \
-     disttool_definebyquery_camera.sql \
-     disttool_definebyquery_chip.sql \
-     disttool_definebyquery_diff.sql \
-     disttool_definebyquery_fake.sql \
-     disttool_definebyquery_raw.sql \
-     disttool_definebyquery_stack.sql \
-     disttool_definebyquery_warp.sql \
-     disttool_definebyquery_SSdiff.sql \
-     disttool_defineinterest.sql \
-     disttool_listfilesets.sql \
-     disttool_listinterests.sql \
-     disttool_pending_camera.sql \
-     disttool_pending_chip.sql \
-     disttool_pending_diff.sql \
-     disttool_pending_fake.sql \
-     disttool_pending_raw.sql \
-     disttool_pending_stack.sql \
-     disttool_pending_warp.sql \
-     disttool_pending_SSdiff.sql \
-     disttool_pendingfileset.sql \
-     disttool_pendingdest.sql \
-     disttool_pendingcleanup.sql \
-     disttool_processedcomponent.sql \
-     disttool_queuercrun.sql \
-     disttool_revertrcrun.sql \
-     disttool_revertrun.sql \
-     disttool_revertcomponent.sql \
-     disttool_revertfileset.sql \
-     disttool_toadvance.sql \
-     disttool_updateinterest.sql \
-     disttool_updatercrun.sql \
-     dqstatstool_createbundle.sql \
-     dqstatstool_definebyquery.sql \
-     dqstatstool_get_contents.sql \
-     dqstatstool_get_run.sql \
-     faketool_change_exp_state.sql \
-     faketool_change_imfile_data_state.sql \
-     faketool_completely_processed_exp.sql \
-     faketool_donecleanup.sql \
-     faketool_export_processed_imfile.sql \
-     faketool_export_run.sql \
-     faketool_find_camrun.sql \
-     faketool_find_pendingexp.sql \
-     faketool_pendingcleanupimfile.sql \
-     faketool_pendingcleanuprun.sql \
-     faketool_pendingimfile.sql \
-     faketool_processedimfile.sql \
-     faketool_queue_cam_id.sql \
-     faketool_revertprocessedimfile.sql \
-     faketool_unmasked.sql \
-     flatcorr_chiprundone.sql \
-     flatcorr_camerarundone.sql \
-     flatcorr_pendingprocess.sql \
-     flatcorr_inputimfile.sql \
-     flatcorr_dropchip.sql \
-     flatcorr_dropcamera.sql \
-     magictool_addmask.sql \
-     magictool_restore_camera.sql \
-     magictool_restore_chip.sql \
-     magictool_restore_diff.sql \
-     magictool_restore_raw.sql \
-     magictool_restore_warp.sql \
-     magictool_create_tmp_warpcomplete.sql \
-     magictool_definebyquery_insert.sql \
-     magictool_definebyquery_select.sql \
-     magictool_inputs.sql \
-     magictool_inputskyfile.sql \
-     magictool_mask.sql \
-     magictool_tomask.sql \
-     magictool_toprocess_inputs.sql \
-     magictool_toprocess_runs.sql \
-     magictool_toprocess_tree.sql \
-     magictool_toskyfilemask.sql \
-     magictool_totree.sql \
-     magictool_diffskyfile.sql \
-     magictool_warpskyfile.sql \
-     magictool_chipprocessedimfile.sql \
-     magictool_rawimfile.sql \
-     magictool_revertnode.sql \
-     magictool_exposure.sql \
-     magicdstool_clearstatefaults.sql \
-     magicdstool_completed_runs.sql \
-     magicdstool_completedrevert.sql \
-     magicdstool_definebyquery_raw.sql \
-     magicdstool_definebyquery_chip.sql \
-     magicdstool_definebyquery_camera.sql \
-     magicdstool_definebyquery_warp.sql \
-     magicdstool_definebyquery_diff.sql \
-     magicdstool_definecopy_chip.sql \
-     magicdstool_definecopy_warp.sql \
-     magicdstool_getrunids.sql \
-     magicdstool_getskycells.sql \
-     magicdstool_revertdestreakedfile.sql \
-     magicdstool_tocleanup.sql \
-     magicdstool_todestreak_camera.sql \
-     magicdstool_todestreak_chip.sql \
-     magicdstool_todestreak_diff.sql \
-     magicdstool_todestreak_raw.sql \
-     magicdstool_todestreak_warp.sql \
-     magicdstool_toremove.sql \
-     magicdstool_torevert_raw.sql \
-     magicdstool_torevert_chip.sql \
-     magicdstool_torevert_camera.sql \
-     magicdstool_torevert_warp.sql \
-     magicdstool_torevert_diff.sql \
-     pstamptool_completedreq.sql \
-     pstamptool_datastore.sql \
-     pstamptool_getdependent.sql \
-     pstamptool_listjob.sql \
-     pstamptool_pendingcleanup.sql \
-     pstamptool_pendingdependent.sql \
-     pstamptool_pendingjob.sql \
-     pstamptool_pendingreq.sql \
-     pstamptool_project.sql \
-     pstamptool_revertdependent.sql \
-     pstamptool_revertjob.sql \
-     pstamptool_revertreq.sql \
-     pstamptool_revertreq_deletejobs.sql \
-     pstamptool_updatejob.sql \
-     pxadmin_create_tables.sql \
-     pxadmin_create_mirror_tables.sql \
-     pxadmin_drop_tables.sql \
-     pxadmin_update_version.sql \
-     pubtool_definerun.sql \
-     pubtool_pending.sql \
-     pubtool_revert.sql \
-     pztool_find_completed_exp.sql \
-     pztool_pendingimfile.sql \
-     pztool_revert_downloadimfile_faults.sql \
-     pztool_revert_fileset_faults.sql \
-     pztool_revertcopied.sql \
-     receivetool_list.sql \
-     receivetool_addfileset.sql \
-     receivetool_pendingfileset.sql \
-     receivetool_pendingfile.sql \
-     receivetool_revert.sql \
-     receivetool_toadvance.sql \
-     regtool_create_dup_table.sql \
-     regtool_export_exp.sql \
-     regtool_export_imfile.sql \
-     regtool_pendingexp.sql \
-     regtool_pendingimfile.sql \
-     regtool_populate_dup_table.sql \
-     regtool_processedexp.sql \
-     regtool_processedimfile.sql \
-     regtool_revertprocessedexp.sql \
-     regtool_revertprocessedimfile.sql \
-     regtool_updateprocessedimfile.sql \
-     regtool_pendingcompressimfile.sql \
-     regtool_finishcompressexp.sql \
-     regtool_updatebyquery.sql \
-     regtool_updatebyqueryimfile.sql \
-     stacktool_definebyquery_insert.sql \
-     stacktool_definebyquery_insert_random_part1.sql \
-     stacktool_definebyquery_insert_random_part2.sql \
-     stacktool_definebyquery_select.sql \
-     stacktool_definebyquery_test.sql \
-     stacktool_donecleanup.sql \
-     stacktool_export_input_skyfile.sql \
-     stacktool_export_run.sql \
-     stacktool_export_sum_skyfile.sql \
-     stacktool_find_complete_warps.sql \
-     stacktool_inputskyfile.sql \
-     stacktool_pendingcleanuprun.sql \
-     stacktool_pendingcleanupskyfile.sql \
-     stacktool_revertsumskyfile_delete.sql \
-     stacktool_sumskyfile.sql \
-     stacktool_tosum.sql \
-     staticskytool_definebyquery_select.sql \
-     staticskytool_definebyquery_inputs.sql \
-     staticskytool_inputs.sql \
-     staticskytool_todo.sql \
-     staticskytool_result.sql \
-     staticskytool_revert.sql \
-     warptool_change_skyfile_data_state.sql \
-     warptool_change_run_state.sql \
-     warptool_definebyquery.sql \
-     warptool_donecleanup.sql \
-     warptool_exp.sql \
-     warptool_export_imfile.sql \
-     warptool_export_run.sql \
-     warptool_export_skyfile.sql \
-     warptool_export_skycell_map.sql \
-     warptool_finished_run_select.sql \
-     warptool_finish_run.sql \
-     warptool_coalesce_run.sql \
-     warptool_imfile.sql \
-     warptool_listrun.sql \
-     warptool_pendingcleanuprun.sql \
-     warptool_pendingcleanupskyfile.sql \
-     warptool_revertoverlap.sql \
-     warptool_revertwarped_delete.sql \
-     warptool_revertwarped_updated.sql \
-     warptool_runstate.sql \
-     warptool_scmap.sql \
-     warptool_setskyfiletoupdate.sql \
-     warptool_tooverlap.sql \
-     warptool_towarped.sql \
-     warptool_updateskyfile.sql \
-     warptool_warped.sql
+	addtool_checkminidvodbaddrun.sql \
+	addtool_donecleanup.sql \
+	addtool_find_cam_id.sql \
+	addtool_find_pendingexp.sql \
+	addtool_find_pendingmergeprocess.sql \
+	addtool_find_processedexp.sql \
+	addtool_find_minidvodbprocessed.sql \
+	addtool_find_minidvodbrun.sql \
+	addtool_pendingcleanupexp.sql \
+	addtool_pendingcleanuprun.sql \
+	addtool_queue_cam_id.sql \
+	addtool_queue_minidvodbrun.sql \
+	addtool_revertminidvodbprocessed.sql \
+	addtool_revertprocessedexp.sql \
+	bgtool_advancechip.sql \
+	bgtool_advancewarp.sql \
+	bgtool_chipinputs.sql \
+	bgtool_chip.sql \
+	bgtool_cleanedchip.sql \
+	bgtool_cleanedwarp.sql \
+	bgtool_definechip.sql \
+	bgtool_definewarp.sql \
+	bgtool_revertchip.sql \
+	bgtool_revertwarp.sql \
+	bgtool_tochip.sql \
+	bgtool_tocleanchip.sql \
+	bgtool_tocleanwarp.sql \
+	bgtool_towarp.sql \
+	bgtool_warpinputs.sql \
+	bgtool_warp.sql \
+	camtool_donecleanup.sql \
+	camtool_find_chip_id.sql \
+	camtool_pendingexp.sql \
+	camtool_find_pendingimfile.sql \
+	camtool_find_processedexp.sql \
+	camtool_pendingcleanupexp.sql \
+	camtool_pendingcleanuprun.sql \
+	camtool_queue_chip_id.sql \
+	camtool_revertprocessedexp.sql \
+	camtool_export_run.sql \
+	camtool_export_processed_exp.sql \
+	chiptool_change_exp_state.sql \
+	chiptool_change_imfile_data_state.sql \
+	chiptool_completely_processed_exp.sql \
+	chiptool_coalesce_run.sql \
+	chiptool_definecopy.sql \
+	chiptool_donecleanup.sql \
+	chiptool_find_rawexp.sql \
+	chiptool_listrun.sql \
+	chiptool_pendingcleanupimfile.sql \
+	chiptool_pendingcleanuprun.sql \
+	chiptool_pendingimfile.sql \
+	chiptool_processedimfile.sql \
+	chiptool_revertprocessedimfile.sql \
+	chiptool_revertupdatedimfile.sql \
+	chiptool_run.sql \
+	chiptool_runstate.sql \
+	chiptool_setimfiletoupdate.sql \
+	chiptool_export_run.sql \
+	chiptool_export_imfile.sql \
+	chiptool_export_processed_imfile.sql \
+	chiptool_unmasked.sql \
+	detselect_search.sql \
+	detselect_select.sql \
+	dettool_addprocessedexp.sql \
+	dettool_childlessrun.sql \
+	dettool_definebydetrun.sql \
+	dettool_detrunsummary.sql \
+	dettool_find_completed_runs.sql \
+	dettool_input.sql \
+	dettool_normalizedexp.sql \
+	dettool_normalizedimfile.sql \
+	dettool_normalizedstat.sql \
+	dettool_pending.sql \
+	dettool_pendingcleanup_detrunsummary.sql \
+	dettool_pendingcleanup_normalizedexp.sql \
+	dettool_pendingcleanup_normalizedimfile.sql \
+	dettool_pendingcleanup_normalizedstat.sql \
+	dettool_pendingcleanup_processedexp.sql \
+	dettool_pendingcleanup_processedimfile.sql \
+	dettool_pendingcleanup_residexp.sql \
+	dettool_pendingcleanup_residimfile.sql \
+	dettool_pendingcleanup_stacked.sql \
+	dettool_processedimfile.sql \
+	dettool_raw.sql \
+	dettool_residexp.sql \
+	dettool_residimfile.sql \
+	dettool_revertdetrunsummary.sql \
+	dettool_revertnormalizedexp.sql \
+	dettool_revertnormalizedimfile.sql \
+	dettool_revertnormalizedstat.sql \
+	dettool_revertprocessedexp.sql \
+	dettool_revertprocessedimfile.sql \
+	dettool_revertresidexp.sql \
+	dettool_revertresidimfile.sql \
+	dettool_revertstacked.sql \
+	dettool_runs.sql \
+	dettool_stacked.sql \
+	dettool_start_new_iteration.sql \
+	dettool_stop_completed_correct_runs.sql \
+	dettool_tocorrectexp.sql \
+	dettool_tocorrectimfile.sql \
+	dettool_todetrunsummary.sql \
+	dettool_tonormalize.sql \
+	dettool_tonormalizedexp.sql \
+	dettool_tonormalizedstat.sql \
+	dettool_toprocessedexp.sql \
+	dettool_toprocessedimfile.sql \
+	dettool_toresidexp.sql \
+	dettool_toresidimfile.sql \
+	dettool_tostacked.sql \
+	difftool_change_skyfile_data_state.sql \
+	difftool_change_run_state.sql \
+	difftool_completed_runs.sql \
+	difftool_coalesce_run.sql \
+	difftool_definewarpstack_part1.sql \
+	difftool_definewarpstack_part2.sql \
+	difftool_definewarpstack_temp_create.sql \
+	difftool_definewarpwarp_temp_create.sql \
+	difftool_definewarpwarp_temp_insert.sql \
+	difftool_definewarpwarp_select.sql \
+	difftool_definewarpwarp_insert.sql \
+	difftool_definestackstack_part0.sql \
+	difftool_definestackstack_part1.sql \
+	difftool_donecleanup.sql \
+	difftool_export_input_skyfile.sql \
+	difftool_export_run.sql \
+	difftool_export_skyfile.sql \
+	difftool_inputskyfile.sql \
+	difftool_listrun.sql \
+	difftool_pendingcleanuprun.sql \
+	difftool_pendingcleanupskyfile.sql \
+	difftool_revertdiffskyfile_delete.sql \
+	difftool_revertdiffskyfile_updated.sql \
+	difftool_setskyfiletoupdate.sql \
+	difftool_skyfile.sql \
+	difftool_todiffskyfile.sql \
+	difftool_tosummary.sql \
+	difftool_addsummary.sql \
+	disttool_definebyquery_camera.sql \
+	disttool_definebyquery_chip.sql \
+	disttool_definebyquery_diff.sql \
+	disttool_definebyquery_fake.sql \
+	disttool_definebyquery_raw.sql \
+	disttool_definebyquery_stack.sql \
+	disttool_definebyquery_warp.sql \
+	disttool_definebyquery_SSdiff.sql \
+	disttool_defineinterest.sql \
+	disttool_listfilesets.sql \
+	disttool_listinterests.sql \
+	disttool_pending_camera.sql \
+	disttool_pending_chip.sql \
+	disttool_pending_diff.sql \
+	disttool_pending_fake.sql \
+	disttool_pending_raw.sql \
+	disttool_pending_stack.sql \
+	disttool_pending_warp.sql \
+	disttool_pending_SSdiff.sql \
+	disttool_pendingfileset.sql \
+	disttool_pendingdest.sql \
+	disttool_pendingcleanup.sql \
+	disttool_processedcomponent.sql \
+	disttool_queuercrun.sql \
+	disttool_revertrcrun.sql \
+	disttool_revertrun.sql \
+	disttool_revertcomponent.sql \
+	disttool_revertfileset.sql \
+	disttool_toadvance.sql \
+	disttool_updateinterest.sql \
+	disttool_updatercrun.sql \
+	dqstatstool_createbundle.sql \
+	dqstatstool_definebyquery.sql \
+	dqstatstool_get_contents.sql \
+	dqstatstool_get_run.sql \
+	faketool_change_exp_state.sql \
+	faketool_change_imfile_data_state.sql \
+	faketool_completely_processed_exp.sql \
+	faketool_donecleanup.sql \
+	faketool_export_processed_imfile.sql \
+	faketool_export_run.sql \
+	faketool_find_camrun.sql \
+	faketool_find_pendingexp.sql \
+	faketool_pendingcleanupimfile.sql \
+	faketool_pendingcleanuprun.sql \
+	faketool_pendingimfile.sql \
+	faketool_processedimfile.sql \
+	faketool_queue_cam_id.sql \
+	faketool_revertprocessedimfile.sql \
+	faketool_unmasked.sql \
+	flatcorr_chiprundone.sql \
+	flatcorr_camerarundone.sql \
+	flatcorr_pendingprocess.sql \
+	flatcorr_inputimfile.sql \
+	flatcorr_dropchip.sql \
+	flatcorr_dropcamera.sql \
+	magictool_addmask.sql \
+	magictool_restore_camera.sql \
+	magictool_restore_chip.sql \
+	magictool_restore_diff.sql \
+	magictool_restore_raw.sql \
+	magictool_restore_warp.sql \
+	magictool_create_tmp_warpcomplete.sql \
+	magictool_definebyquery_insert.sql \
+	magictool_definebyquery_select.sql \
+	magictool_inputs.sql \
+	magictool_inputskyfile.sql \
+	magictool_mask.sql \
+	magictool_tomask.sql \
+	magictool_toprocess_inputs.sql \
+	magictool_toprocess_runs.sql \
+	magictool_toprocess_tree.sql \
+	magictool_toskyfilemask.sql \
+	magictool_totree.sql \
+	magictool_diffskyfile.sql \
+	magictool_warpskyfile.sql \
+	magictool_chipprocessedimfile.sql \
+	magictool_rawimfile.sql \
+	magictool_revertnode.sql \
+	magictool_exposure.sql \
+	magicdstool_clearstatefaults.sql \
+	magicdstool_completed_runs.sql \
+	magicdstool_completedrevert.sql \
+	magicdstool_definebyquery_raw.sql \
+	magicdstool_definebyquery_chip.sql \
+	magicdstool_definebyquery_camera.sql \
+	magicdstool_definebyquery_warp.sql \
+	magicdstool_definebyquery_diff.sql \
+	magicdstool_definecopy_chip.sql \
+	magicdstool_definecopy_warp.sql \
+	magicdstool_getrunids.sql \
+	magicdstool_getskycells.sql \
+	magicdstool_revertdestreakedfile.sql \
+	magicdstool_tocleanup.sql \
+	magicdstool_todestreak_camera.sql \
+	magicdstool_todestreak_chip.sql \
+	magicdstool_todestreak_diff.sql \
+	magicdstool_todestreak_raw.sql \
+	magicdstool_todestreak_warp.sql \
+	magicdstool_toremove.sql \
+	magicdstool_torevert_raw.sql \
+	magicdstool_torevert_chip.sql \
+	magicdstool_torevert_camera.sql \
+	magicdstool_torevert_warp.sql \
+	magicdstool_torevert_diff.sql \
+	pstamptool_completedreq.sql \
+	pstamptool_datastore.sql \
+	pstamptool_getdependent.sql \
+	pstamptool_listjob.sql \
+	pstamptool_pendingcleanup.sql \
+	pstamptool_pendingdependent.sql \
+	pstamptool_pendingjob.sql \
+	pstamptool_pendingreq.sql \
+	pstamptool_project.sql \
+	pstamptool_revertdependent.sql \
+	pstamptool_revertjob.sql \
+	pstamptool_revertreq.sql \
+	pstamptool_revertreq_deletejobs.sql \
+	pstamptool_updatejob.sql \
+	pxadmin_create_tables.sql \
+	pxadmin_create_mirror_tables.sql \
+	pxadmin_drop_tables.sql \
+	pxadmin_update_version.sql \
+	pubtool_definerun.sql \
+	pubtool_pending.sql \
+	pubtool_revert.sql \
+	pztool_find_completed_exp.sql \
+	pztool_pendingimfile.sql \
+	pztool_revert_downloadimfile_faults.sql \
+	pztool_revert_fileset_faults.sql \
+	pztool_revertcopied.sql \
+	receivetool_list.sql \
+	receivetool_addfileset.sql \
+	receivetool_pendingfileset.sql \
+	receivetool_pendingfile.sql \
+	receivetool_revert.sql \
+	receivetool_toadvance.sql \
+	regtool_create_dup_table.sql \
+	regtool_export_exp.sql \
+	regtool_export_imfile.sql \
+	regtool_pendingexp.sql \
+	regtool_pendingimfile.sql \
+	regtool_populate_dup_table.sql \
+	regtool_processedexp.sql \
+	regtool_processedimfile.sql \
+	regtool_revertprocessedexp.sql \
+	regtool_revertprocessedimfile.sql \
+	regtool_updateprocessedimfile.sql \
+	regtool_pendingcompressimfile.sql \
+	regtool_finishcompressexp.sql \
+	regtool_updatebyquery.sql \
+	regtool_updatebyqueryimfile.sql \
+	stacktool_associationdefine_select.sql \
+	stacktool_definebyquery_insert.sql \
+	stacktool_definebyquery_insert_random_part1.sql \
+	stacktool_definebyquery_insert_random_part2.sql \
+	stacktool_definebyquery_select.sql \
+	stacktool_definebyquery_test.sql \
+	stacktool_donecleanup.sql \
+	stacktool_export_input_skyfile.sql \
+	stacktool_export_run.sql \
+	stacktool_export_sum_skyfile.sql \
+	stacktool_find_complete_warps.sql \
+	stacktool_inputskyfile.sql \
+	stacktool_pendingcleanuprun.sql \
+	stacktool_pendingcleanupskyfile.sql \
+	stacktool_revertsumskyfile_delete.sql \
+	stacktool_sumskyfile.sql \
+	stacktool_sassskyfile.sql \
+	stacktool_tosum.sql \
+	stacktool_tosummary.sql \
+	stacktool_addsummary.sql \
+	staticskytool_definebyquery_select.sql \
+	staticskytool_definebyquery_inputs.sql \
+	staticskytool_inputs.sql \
+	staticskytool_todo.sql \
+	staticskytool_result.sql \
+	staticskytool_revert.sql \
+	warptool_change_skyfile_data_state.sql \
+	warptool_change_run_state.sql \
+	warptool_definebyquery.sql \
+	warptool_donecleanup.sql \
+	warptool_exp.sql \
+	warptool_export_imfile.sql \
+	warptool_export_run.sql \
+	warptool_export_skyfile.sql \
+	warptool_export_skycell_map.sql \
+	warptool_finished_run_select.sql \
+	warptool_finish_run.sql \
+	warptool_coalesce_run.sql \
+	warptool_imfile.sql \
+	warptool_listrun.sql \
+	warptool_pendingcleanuprun.sql \
+	warptool_pendingcleanupskyfile.sql \
+	warptool_revertoverlap.sql \
+	warptool_revertwarped_delete.sql \
+	warptool_revertwarped_updated.sql \
+	warptool_runstate.sql \
+	warptool_scmap.sql \
+	warptool_setskyfiletoupdate.sql \
+	warptool_tooverlap.sql \
+	warptool_towarped.sql \
+	warptool_updateskyfile.sql \
+	warptool_warped.sql \
+	warptool_tosummary.sql \
+	warptool_addsummary.sql \
+	diffphottool_definerun.sql \
+	diffphottool_input.sql \
+	diffphottool_pending.sql \
+	diffphottool_advance.sql \
+	diffphottool_revert.sql \
+	diffphottool_data.sql
Index: /branches/pap/ippTools/share/addtool_checkminidvodbaddrun.sql
===================================================================
--- /branches/pap/ippTools/share/addtool_checkminidvodbaddrun.sql	(revision 28484)
+++ /branches/pap/ippTools/share/addtool_checkminidvodbaddrun.sql	(revision 28484)
@@ -0,0 +1,20 @@
+-- this selects the minidvodbs that have complete addRuns
+SELECT minidvodb_id, minidvodb_name, minidvodb_path, mergedvodb_path, state, minidbg 
+AS minidvodb_group, (cnt - cnt2) as not_full, cnt as addRun_count
+FROM minidvodbRun 
+JOIN 
+   (SELECT minidvodb_name, count(state) as cnt2, cnt, minidbg, minidbn 
+   FROM addRun LEFT JOIN 
+       -- this select grabs (and counts) all the addRuns with any state
+       -- and groups them by the name
+       (SELECT minidvodb_group as minidbg, minidvodb_name as minidbn, count(state) 
+        AS cnt 
+        FROM addRun 
+        GROUP BY minidvodb_name ) 
+   AS foo1 ON minidvodb_name = minidbn
+        -- the second select grabs and counts all the addRuns with state
+        -- of 'full'         
+   WHERE addRun.state = 'full' 
+   GROUP BY minidvodb_name ) 
+AS foo2 
+USING(minidvodb_name) 
Index: /branches/pap/ippTools/share/addtool_find_minidvodbprocessed.sql
===================================================================
--- /branches/pap/ippTools/share/addtool_find_minidvodbprocessed.sql	(revision 28484)
+++ /branches/pap/ippTools/share/addtool_find_minidvodbprocessed.sql	(revision 28484)
@@ -0,0 +1,8 @@
+SELECT
+    minidvodbProcessed.*,
+    minidvodbRun.minidvodb_name	
+    minidvodbRun.minidvodb_group
+FROM minidvodbProcessed
+JOIN minidvodbRun
+    USING(minidvodb_id)
+
Index: /branches/pap/ippTools/share/addtool_find_minidvodbrun.sql
===================================================================
--- /branches/pap/ippTools/share/addtool_find_minidvodbrun.sql	(revision 28484)
+++ /branches/pap/ippTools/share/addtool_find_minidvodbrun.sql	(revision 28484)
@@ -0,0 +1,5 @@
+SELECT
+    minidvodbRun.*
+FROM minidvodbRun
+
+
Index: /branches/pap/ippTools/share/addtool_find_minidvodbrun2.sql
===================================================================
--- /branches/pap/ippTools/share/addtool_find_minidvodbrun2.sql	(revision 28484)
+++ /branches/pap/ippTools/share/addtool_find_minidvodbrun2.sql	(revision 28484)
@@ -0,0 +1,23 @@
+-- this selects the minidvodbs that have complete addRuns
+SELECT minidvodb_name, minidvodb_path, mergedvodb_path, state, minidbg 
+AS minidvodb_group 
+FROM minidvodbRun 
+JOIN 
+   (SELECT minidvodb_name, count(state) as cnt2, cnt, minidbg, minidbn 
+   FROM addRun LEFT JOIN 
+       -- this select grabs (and counts) all the addRuns with any state
+       -- and groups them by the name
+       (SELECT minidvodb_group as minidbg, minidvodb_name as minidbn, count(state) 
+        AS cnt 
+        FROM addRun 
+        GROUP BY minidvodb_name ) 
+   AS foo1 ON minidvodb_name = minidbn
+        -- the second select grabs and counts all the addRuns with state
+        -- of 'full'	     
+   WHERE addRun.state = 'full' 
+   GROUP BY minidvodb_name ) 
+AS foo2 
+USING(minidvodb_name) 
+WHERE cnt2 = cnt
+       -- the 3rd select only grabs the columns where the counts are the same
+       -- (same number of 'full' and any state)
Index: /branches/pap/ippTools/share/addtool_find_pendingmergeprocess.sql
===================================================================
--- /branches/pap/ippTools/share/addtool_find_pendingmergeprocess.sql	(revision 28484)
+++ /branches/pap/ippTools/share/addtool_find_pendingmergeprocess.sql	(revision 28484)
@@ -0,0 +1,5 @@
+SELECT minidvodbRun.* 
+FROM minidvodbRun 
+LEFT JOIN minidvodbProcessed
+USING(minidvodb_id)
+WHERE (minidvodbRun.state = 'to_be_merged' AND minidvodbProcessed.minidvodb_id IS NULL)
Index: /branches/pap/ippTools/share/addtool_find_processedexp.sql
===================================================================
--- /branches/pap/ippTools/share/addtool_find_processedexp.sql	(revision 28483)
+++ /branches/pap/ippTools/share/addtool_find_processedexp.sql	(revision 28484)
@@ -2,6 +2,8 @@
     addProcessedExp.*,
     addRun.workdir
-FROM addRun
-JOIN camProcessedExp
+FROM addProcessedExp
+JOIN addRun
+    USING(add_id)
+JOIN camRun
     USING(cam_id)
 JOIN chipRun
Index: /branches/pap/ippTools/share/addtool_queue_cam_id.sql
===================================================================
--- /branches/pap/ippTools/share/addtool_queue_cam_id.sql	(revision 28483)
+++ /branches/pap/ippTools/share/addtool_queue_cam_id.sql	(revision 28484)
@@ -11,5 +11,8 @@
         '%s',           -- dvodb 
         '%s',           -- note
-	%d		-- image_only
+	%d,		-- image_only
+	%d,		-- minidvodb
+	'%s',           -- minidvodb_group 
+ 	'%s'	        -- minidvodb_name
     FROM camRun
     WHERE
Index: /branches/pap/ippTools/share/addtool_queue_minidvodbrun.sql
===================================================================
--- /branches/pap/ippTools/share/addtool_queue_minidvodbrun.sql	(revision 28484)
+++ /branches/pap/ippTools/share/addtool_queue_minidvodbrun.sql	(revision 28484)
@@ -0,0 +1,12 @@
+-- create rcRun's for all destinations that have filesets that they are interested in available
+INSERT INTO minidvodbRun
+SELECT                  -- rows in this select must match rcRun
+    minidvodb_name,
+    minidvodb_path,
+    mergedvodb_path,
+    'new'
+
+FROM addRun 
+LEFT JOIN minidvodbRun USING(minidvodb_name)
+-- WHERE minidvodbRun.minidvodb_name IS NULL
+  
Index: anches/pap/ippTools/share/addtool_reset_faulted_runs.sql
===================================================================
--- /branches/pap/ippTools/share/addtool_reset_faulted_runs.sql	(revision 28483)
+++ 	(revision )
@@ -1,8 +1,0 @@
-UPDATE addRun, addProcessedExp, camRun, chipRun, rawExp
-SET addRun.state = 'new'
-WHERE
-    addRun.add_id = addProcessedExp.add_id
-    AND addRun.cam_id = camRun.cam_id
-    AND camRun.chip_id = chipRun.chip_id
-    AND chipRun.exp_id = rawExp.exp_id
-    AND addProcessedExp.fault != 0
Index: /branches/pap/ippTools/share/addtool_revertminidvodbprocessed.sql
===================================================================
--- /branches/pap/ippTools/share/addtool_revertminidvodbprocessed.sql	(revision 28484)
+++ /branches/pap/ippTools/share/addtool_revertminidvodbprocessed.sql	(revision 28484)
@@ -0,0 +1,7 @@
+DELETE FROM minidvodbProcessed
+USING minidvodbProcessed, minidvodbRun, addRun
+WHERE
+    minidvodbProcessed.minidvodb_id = minidvodbRun.minidvodb_id
+    AND addRun.minidvodb_name = minidvodbRun.minidvodb_name
+    AND minidvodbProcessed.fault != 0
+    AND minidvodbRun.state = 'new'
Index: /branches/pap/ippTools/share/addtool_revertprocessedexp.sql
===================================================================
--- /branches/pap/ippTools/share/addtool_revertprocessedexp.sql	(revision 28483)
+++ /branches/pap/ippTools/share/addtool_revertprocessedexp.sql	(revision 28484)
@@ -7,2 +7,3 @@
     AND chipRun.exp_id = rawExp.exp_id
     AND addProcessedExp.fault != 0
+    AND addRun.state = 'new'
Index: /branches/pap/ippTools/share/camtool_addprocessedexp.sql
===================================================================
--- /branches/pap/ippTools/share/camtool_addprocessedexp.sql	(revision 28484)
+++ /branches/pap/ippTools/share/camtool_addprocessedexp.sql	(revision 28484)
@@ -0,0 +1,23 @@
+SELECT
+    camRun.*,
+    rawExp.exp_tag,
+    rawExp.exp_id,
+    rawExp.exp_name,
+    rawExp.camera,
+    rawExp.telescope,
+    rawExp.filelevel,
+    chipRun.magicked AS chip_magicked,
+    camProcessedExp.path_base
+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
Index: anches/pap/ippTools/share/camtool_find_pendingexp.sql
===================================================================
--- /branches/pap/ippTools/share/camtool_find_pendingexp.sql	(revision 28483)
+++ 	(revision )
@@ -1,26 +1,0 @@
--- this query is used by both camtool -pendingexp & camtool -addprocessedexp it
--- does a little more work then is necessary for -addprocessed but it seems
--- "cleaner" to use the same query for both cases 
-SELECT
-    camRun.*,
-    rawExp.exp_tag,
-    rawExp.exp_id,
-    rawExp.exp_name,
-    rawExp.camera,
-    rawExp.telescope,
-    rawExp.filelevel,
-    chipRun.magicked AS chip_magicked,
-    camProcessedExp.path_base
-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
Index: /branches/pap/ippTools/share/camtool_pendingexp.sql
===================================================================
--- /branches/pap/ippTools/share/camtool_pendingexp.sql	(revision 28484)
+++ /branches/pap/ippTools/share/camtool_pendingexp.sql	(revision 28484)
@@ -0,0 +1,27 @@
+SELECT
+    camRun.*,
+    rawExp.exp_tag,
+    rawExp.exp_id,
+    rawExp.exp_name,
+    rawExp.camera,
+    rawExp.telescope,
+    rawExp.filelevel,
+    chipRun.magicked AS chip_magicked,
+    camProcessedExp.path_base,
+    IFNULL(Label.priority, 10000) AS priority
+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
+LEFT JOIN Label
+    ON camRun.label = Label.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 (Label.active OR Label.active IS NULL)
Index: anches/pap/ippTools/share/camtool_reset_faulted_runs.sql
===================================================================
--- /branches/pap/ippTools/share/camtool_reset_faulted_runs.sql	(revision 28483)
+++ 	(revision )
@@ -1,7 +1,0 @@
-UPDATE camRun, camProcessedExp, chipRun, rawExp
-SET camRun.state = 'new'
-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
Index: /branches/pap/ippTools/share/camtool_revertprocessedexp.sql
===================================================================
--- /branches/pap/ippTools/share/camtool_revertprocessedexp.sql	(revision 28483)
+++ /branches/pap/ippTools/share/camtool_revertprocessedexp.sql	(revision 28484)
@@ -6,2 +6,3 @@
     AND chipRun.exp_id = rawExp.exp_id
     AND camProcessedExp.fault != 0
+    AND camRun.state = 'new'
Index: /branches/pap/ippTools/share/chiptool_processedimfile.sql
===================================================================
--- /branches/pap/ippTools/share/chiptool_processedimfile.sql	(revision 28483)
+++ /branches/pap/ippTools/share/chiptool_processedimfile.sql	(revision 28484)
@@ -29,5 +29,6 @@
     rawImfile.magicked AS raw_magicked,
     rawImfile.burntool_state,
-    magicDSRun.state AS dsRun_state
+    magicDSRun.state AS dsRun_state,
+    magicDSRun.magic_ds_id
 FROM chipRun
 JOIN chipImfile
Index: /branches/pap/ippTools/share/diffphottool_advance.sql
===================================================================
--- /branches/pap/ippTools/share/diffphottool_advance.sql	(revision 28484)
+++ /branches/pap/ippTools/share/diffphottool_advance.sql	(revision 28484)
@@ -0,0 +1,12 @@
+SELECT
+    diff_phot_id
+FROM diffPhotRun
+JOIN diffSkyfile USING(diff_id)
+LEFT JOIN diffPhotSkyfile USING(diff_phot_id, skycell_id)
+WHERE diffPhotRun.state = 'new'
+    AND diffSkyfile.fault = 0
+    AND diffSkyfile.quality = 0
+-- WHERE hook %s
+GROUP BY diff_phot_id
+HAVING COUNT(diffPhotSkyfile.skycell_id) = COUNT(diffSkyfile.skycell_id)
+    AND SUM(IF(diffPhotSkyfile.fault > 0, 1, 0)) = 0
Index: /branches/pap/ippTools/share/diffphottool_data.sql
===================================================================
--- /branches/pap/ippTools/share/diffphottool_data.sql	(revision 28484)
+++ /branches/pap/ippTools/share/diffphottool_data.sql	(revision 28484)
@@ -0,0 +1,60 @@
+SELECT DISTINCT
+    diffPhotSkyfile.*,
+    diffRun.tess_id,
+    diffPhotRun.state,
+    diffPhotRun.workdir,
+    diffPhotRun.label,
+    diffRun.bothways,
+    diffRun.diff_mode,
+    -- The following are only valid for warps
+    -- XXX This needs to be more clever to handle diffs between stacks
+    -- Zero points are appropriate for both forward and backward diffs
+    camProcessedInput.zpt_obs,
+    camProcessedInput.zpt_stdev,
+    camProcessedInput.zpt_lq,
+    camProcessedInput.zpt_uq,
+    rawInput.comment,
+    rawInput.exp_time,
+    rawInput.camera,
+    rawInput.exp_name AS exp_name_1,
+    rawInput.exp_id AS exp_id_1,
+    chipInput.chip_id AS chip_id_1,
+    camInput.cam_id AS cam_id_1,
+    fakeInput.fake_id AS fake_id_1,
+    camProcessedInput.sigma_ra AS sigma_ra_1,
+    camProcessedInput.sigma_dec AS sigma_dec_1,
+    rawTemplate.exp_name AS exp_name_2,
+    rawTemplate.exp_id AS exp_id_2,
+    chipTemplate.chip_id AS chip_id_2,
+    camTemplate.cam_id AS cam_id_2,
+    fakeTemplate.fake_id AS fake_id_2,
+    camProcessedTemplate.sigma_ra AS sigma_ra_2,
+    camProcessedTemplate.sigma_dec AS sigma_dec_2
+FROM diffPhotRun
+JOIN diffPhotSkyfile USING(diff_phot_id)
+JOIN diffRun USING(diff_id)
+JOIN diffInputSkyfile USING(diff_id, skycell_id)
+LEFT JOIN warpRun AS warpInput
+    ON warpInput.warp_id = diffInputSkyfile.warp1
+LEFT JOIN fakeRun AS fakeInput
+    ON fakeInput.fake_id = warpInput.fake_id
+LEFT JOIN camRun AS camInput
+    ON camInput.cam_id = fakeInput.cam_id
+LEFT JOIN camProcessedExp AS camProcessedInput
+    ON camProcessedInput.cam_id = camInput.cam_id
+LEFT JOIN chipRun AS chipInput
+    ON chipInput.chip_id = camInput.chip_id
+LEFT JOIN rawExp AS rawInput
+    ON rawInput.exp_id = chipInput.exp_id
+LEFT JOIN warpRun AS warpTemplate
+    ON warpTemplate.warp_id = diffInputSkyfile.warp2
+LEFT JOIN fakeRun AS fakeTemplate
+    ON fakeTemplate.fake_id = warpTemplate.fake_id
+LEFT JOIN camRun AS camTemplate
+    ON camTemplate.cam_id = fakeTemplate.cam_id
+LEFT JOIN camProcessedExp AS camProcessedTemplate
+    ON camProcessedTemplate.cam_id = camTemplate.cam_id
+LEFT JOIN chipRun AS chipTemplate
+    ON chipTemplate.chip_id = camTemplate.chip_id
+LEFT JOIN rawExp AS rawTemplate
+    ON rawTemplate.exp_id = chipTemplate.exp_id
Index: /branches/pap/ippTools/share/diffphottool_definerun.sql
===================================================================
--- /branches/pap/ippTools/share/diffphottool_definerun.sql	(revision 28484)
+++ /branches/pap/ippTools/share/diffphottool_definerun.sql	(revision 28484)
@@ -0,0 +1,10 @@
+SELECT DISTINCT
+    diffRun.*
+FROM diffRun
+JOIN diffInputSkyfile USING(diff_id)
+JOIN warpRun ON warpRun.warp_id = diffInputSkyfile.warp1 -- only JOINing inputs, not templates!
+JOIN fakeRun USING(fake_id)
+JOIN camRun USING(cam_id)
+JOIN chipRun USING(chip_id)
+JOIN rawExp USING(exp_id)
+WHERE diffRun.state = 'full'
Index: /branches/pap/ippTools/share/diffphottool_input.sql
===================================================================
--- /branches/pap/ippTools/share/diffphottool_input.sql	(revision 28484)
+++ /branches/pap/ippTools/share/diffphottool_input.sql	(revision 28484)
@@ -0,0 +1,15 @@
+SELECT
+    diffPhotRun.*,
+    diffSkyfile.path_base,
+    diffSkyfile.magicked,
+    diffRun.bothways,
+    rawExp.camera
+FROM diffPhotRun
+JOIN diffRun USING(diff_id)
+JOIN diffSkyfile USING(diff_id)
+JOIN diffInputSkyfile USING(diff_id, skycell_id)
+JOIN warpRun ON warp1 = warp_id -- only JOINing on input warps!
+JOIN fakeRun USING(fake_id)
+JOIN camRun USING(cam_id)
+JOIN chipRun USING(chip_id)
+JOIN rawExp USING(exp_id)
Index: /branches/pap/ippTools/share/diffphottool_pending.sql
===================================================================
--- /branches/pap/ippTools/share/diffphottool_pending.sql	(revision 28484)
+++ /branches/pap/ippTools/share/diffphottool_pending.sql	(revision 28484)
@@ -0,0 +1,13 @@
+SELECT DISTINCT
+    diffPhotRun.*,
+    diffRun.tess_id,
+    diffSkyfile.skycell_id
+FROM diffPhotRun
+JOIN diffRun USING(diff_id)
+JOIN diffSkyfile USING(diff_id)
+LEFT JOIN diffPhotSkyfile USING(diff_phot_id, skycell_id)
+WHERE diffPhotSkyfile.skycell_id IS NULL
+    AND diffRun.state = 'full'
+    AND diffSkyfile.magicked >= 0
+    AND diffSkyfile.fault = 0
+    AND diffSkyfile.quality = 0
Index: /branches/pap/ippTools/share/diffphottool_revert.sql
===================================================================
--- /branches/pap/ippTools/share/diffphottool_revert.sql	(revision 28484)
+++ /branches/pap/ippTools/share/diffphottool_revert.sql	(revision 28484)
@@ -0,0 +1,4 @@
+DELETE diffPhotSkyfile
+FROM diffPhotRun
+JOIN diffPhotSkyfile USING(diff_phot_id)
+WHERE fault != 0
Index: /branches/pap/ippTools/share/difftool_addsummary.sql
===================================================================
--- /branches/pap/ippTools/share/difftool_addsummary.sql	(revision 28484)
+++ /branches/pap/ippTools/share/difftool_addsummary.sql	(revision 28484)
@@ -0,0 +1,5 @@
+INSERT INTO diffSummary (diff_id,projection_cell,path_base) VALUES (
+       %lld,   -- warp_id
+       '%s',   -- projection_cell
+       '%s'    -- path_base
+      )
Index: /branches/pap/ippTools/share/difftool_tosummary.sql
===================================================================
--- /branches/pap/ippTools/share/difftool_tosummary.sql	(revision 28484)
+++ /branches/pap/ippTools/share/difftool_tosummary.sql	(revision 28484)
@@ -0,0 +1,5 @@
+SELECT DISTINCT diff_id,"GPC1" as camera,diffRun.workdir,diffRun.tess_id,diffRun.diff_mode,diffRun.state
+       FROM diffRun 
+       LEFT JOIN diffSummary USING(diff_id)
+WHERE diffRun.state = 'full' AND 
+      diffSummary.projection_cell IS NULL 
Index: /branches/pap/ippTools/share/pubtool_definerun.sql
===================================================================
--- /branches/pap/ippTools/share/pubtool_definerun.sql	(revision 28483)
+++ /branches/pap/ippTools/share/pubtool_definerun.sql	(revision 28484)
@@ -1,4 +1,4 @@
 -- Get runs to publish
-SELECT
+SELECT DISTINCT
     client_id,
     stage_id,
@@ -6,14 +6,20 @@
 FROM (
     -- Get diffs to publish
-    SELECT
+    SELECT DISTINCT
         client_id,
         diff_id AS stage_id,
-        label AS src_label
+        diffRun.label AS src_label
     FROM publishClient
     JOIN diffRun
+    JOIN diffInputSkyfile USING(diff_id)
+    JOIN warpRun ON warpRun.warp_id = diffInputSkyfile.warp1 -- Only JOINing input, not reference!
+    JOIN fakeRun USING(fake_id)
+    JOIN camRun USING(cam_id)
+    JOIN chipRun USING(chip_id)
+    JOIN rawExp USING(exp_id)
     WHERE publishClient.stage = 'diff'
         AND publishClient.active = 1
-        AND diffRun.state = 'full'
-        AND (diffRun.magicked > 0 OR diffRun.diff_mode = 4 OR publishClient.magicked = 0)
+        AND diffRun.state IN ('full', 'cleaned', 'goto_cleaned')
+        AND (diffRun.magicked != 0 OR diffRun.diff_mode = 4 OR publishClient.magicked = 0)
     -- WHERE hook %s
     UNION
@@ -22,14 +28,35 @@
         client_id,
         cam_id AS stage_id,
-        label AS src_label
+        camRun.label AS src_label
     FROM publishClient
     JOIN camRun
+    JOIN chipRun USING(chip_id)
+    JOIN rawExp USING(exp_id)
     WHERE publishClient.stage = 'camera'
         AND publishClient.active = 1
-        AND camRun.state = 'full'
-        AND (camRun.magicked > 0 OR publishClient.magicked = 0)
+        AND camRun.state IN ('full', 'cleaned', 'goto_cleaned')
+        AND (camRun.magicked != 0 OR publishClient.magicked = 0)
+    -- WHERE hook %s
+    UNION
+    -- Get diffphots to publish
+    SELECT DISTINCT
+        client_id,
+        diff_phot_id AS stage_id,
+        diffPhotRun.label AS src_label
+    FROM publishClient
+    JOIN diffPhotRun
+    JOIN diffRun USING(diff_id)
+    JOIN diffInputSkyfile USING(diff_id)
+    JOIN warpRun ON warpRun.warp_id = diffInputSkyfile.warp1 -- Only JOINing input, not reference!
+    JOIN fakeRun USING(fake_id)
+    JOIN camRun USING(cam_id)
+    JOIN chipRun USING(chip_id)
+    JOIN rawExp USING(exp_id)
+    WHERE publishClient.stage = 'diffphot'
+        AND publishClient.active = 1
+        AND diffPhotRun.state IN ('full', 'cleaned', 'goto_cleaned')
+        AND (diffPhotRun.magicked != 0 OR diffRun.diff_mode = 4 OR publishClient.magicked = 0)
     -- WHERE hook %s
     ) AS publishToDo
 -- Only get stuff that hasn't been published
 LEFT JOIN publishRun USING(client_id, stage_id)
-WHERE publishRun.client_id IS NULL
Index: /branches/pap/ippTools/share/pubtool_pending.sql
===================================================================
--- /branches/pap/ippTools/share/pubtool_pending.sql	(revision 28483)
+++ /branches/pap/ippTools/share/pubtool_pending.sql	(revision 28484)
@@ -26,6 +26,6 @@
         AND publishClient.active = 1
         AND publishRun.state = 'new'
-        AND diffRun.state = 'full'
-        AND (diffRun.magicked > 0 OR diffRun.diff_mode = 4 OR publishClient.magicked = 0)
+        AND diffRun.state IN ('full', 'cleaned', 'goto_cleaned')
+        AND (diffRun.magicked != 0 OR diffRun.diff_mode = 4 OR publishClient.magicked = 0)
         -- WHERE hook %s
     UNION
@@ -47,6 +47,32 @@
         AND publishClient.active = 1
         AND publishRun.state ='new'
-        AND camRun.state = 'full'
-        AND (camRun.magicked > 0 OR publishClient.magicked = 0)
+        AND camRun.state IN ('full', 'cleaned', 'goto_cleaned')
+        AND (camRun.magicked != 0 OR publishClient.magicked = 0)
+        -- WHERE hook %s
+    SELECT DISTINCT
+        publishRun.pub_id,
+        publishClient.product,
+        publishClient.stage,
+        publishClient.workdir,
+        diffPhotRun.diff_phot_id AS stage_id,
+        rawExp.camera,
+        rawExp.exp_id
+    FROM publishRun
+    JOIN publishClient USING(client_id)
+    JOIN diffPhotRun
+        ON diffPhotRun.diff_phot_id = publishRun.stage_id
+    JOIN diffRun USING(diff_id)
+    JOIN diffInputSkyfile USING(diff_id)
+    -- Need to do something fancy here to get the camera name for a stack
+    LEFT JOIN warpRun ON warpRun.warp_id = diffInputSkyfile.warp1
+    JOIN fakeRun USING(fake_id)
+    JOIN camRun USING(cam_id)
+    JOIN chipRun USING(chip_id)
+    JOIN rawExp USING(exp_id)
+    WHERE publishClient.stage = 'diffphot'
+        AND publishClient.active = 1
+        AND publishRun.state = 'new'
+        AND diffPhotRun.state IN ('full', 'cleaned', 'goto_cleaned')
+        AND (diffPhotRun.magicked != 0 OR diffRun.diff_mode = 4 OR publishClient.magicked = 0)
         -- WHERE hook %s
 ) AS publishToDo
Index: /branches/pap/ippTools/share/pxadmin_create_tables.sql
===================================================================
--- /branches/pap/ippTools/share/pxadmin_create_tables.sql	(revision 28483)
+++ /branches/pap/ippTools/share/pxadmin_create_tables.sql	(revision 28484)
@@ -981,4 +981,13 @@
 ) ENGINE=innodb DEFAULT CHARSET=latin1;
 
+CREATE TABLE warpSummary (
+       warp_id BIGINT,
+       projection_cell VARCHAR(64) NOT NULL,
+       path_base     VARCHAR(255) NOT NULL,
+       PRIMARY KEY(warp_id),
+       KEY(projection_cell),
+       FOREIGN KEY(warp_id) REFERENCES warpRun(warp_id)
+) ENGINE=innodb DEFAULT CHARSET=latin1;
+
 CREATE TABLE stackRun (
         stack_id BIGINT AUTO_INCREMENT,
@@ -1049,4 +1058,33 @@
         KEY(quality),
         FOREIGN KEY(stack_id) REFERENCES stackRun(stack_id)
+) ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE stackAssociation (
+    sass_id      BIGINT AUTO_INCREMENT,
+    data_group   VARCHAR(64) NOT NULL,
+    projection_cell VARCHAR(64) NOT NULL,
+    tess_id       VARCHAR(64) NOT NULL,
+    filter        VARCHAR(64) NOT NULL,
+    PRIMARY KEY(sass_id),
+    KEY(data_group),
+    KEY(projection_cell),
+    KEY(tess_id)
+) ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE stackAssociationMap (
+    sass_id      BIGINT,
+    stack_id     BIGINT,
+    PRIMARY KEY(sass_id, stack_id),
+    FOREIGN KEY(sass_id) REFERENCES stackAssociation(sass_id),
+    FOREIGN KEY(stack_id) REFERENCES stackRun(stack_id)
+) ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE stackSummary (
+    sass_id     BIGINT,
+    projection_cell VARCHAR(64) NOT NULL,
+    path_base   VARCHAR(255) NOT NULL,
+    PRIMARY KEY(sass_id),
+    KEY(projection_cell),
+    FOREIGN KEY(sass_id) REFERENCES stackAssociation(sass_id)
 ) ENGINE=innodb DEFAULT CHARSET=latin1;
 
@@ -1147,4 +1185,13 @@
 ) ENGINE=innodb DEFAULT CHARSET=latin1;
 
+CREATE TABLE diffSummary (
+    diff_id     BIGINT,
+    projection_cell VARCHAR(64) NOT NULL,
+    path_base   VARCHAR(255) NOT NULL,
+    PRIMARY KEY(diff_id),
+    KEY(projection_cell),
+    FOREIGN KEY(diff_id) REFERENCES diffRun(diff_id)
+) ENGINE=innodb DEFAULT CHARSET=latin1;
+
 CREATE TABLE magicRun (
         magic_id BIGINT AUTO_INCREMENT,
@@ -1364,4 +1411,5 @@
         uri VARCHAR(255),
         outdir     VARCHAR(255),
+        timestamp TIMESTAMP,
         fault SMALLINT,
         PRIMARY KEY(req_id),
@@ -1679,5 +1727,4 @@
 ) ENGINE=innodb DEFAULT CHARSET=latin1;
 
-
 -- Tables to support background restoration
 
@@ -1778,4 +1825,39 @@
 
 
+-- Tables to support (re-)photometry of a diff
+
+CREATE TABLE diffPhotRun (
+    diff_phot_id BIGINT AUTO_INCREMENT, -- Identifier for diffPhotRun
+    diff_id BIGINT NOT NULL,            -- Identifier for diffRun
+    state VARCHAR(64) NOT NULL,         -- State of run
+    workdir VARCHAR(255) NOT NULL, -- working directory
+    label VARCHAR(64),             -- processing label
+    data_group VARCHAR(64),        -- group for data
+    reduction VARCHAR(64),         -- reduction class (for altering recipe)
+    registered TIMESTAMP DEFAULT CURRENT_TIMESTAMP, -- time run was registered
+    note VARCHAR(255),             -- note
+    PRIMARY KEY(diff_phot_id),
+    KEY(diff_id),
+    KEY(state),
+    KEY(label),
+    KEY(data_group),
+    FOREIGN KEY(diff_id) REFERENCES diffRun(diff_id)
+) ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE diffPhotSkyfile (
+    diff_phot_id BIGINT AUTO_INCREMENT, -- Identifier for diffPhotRun
+    skycell_id VARCHAR(64) NOT NULL,            -- Skycell identifier
+    path_base VARCHAR(255) NOT NULL, -- Base of path for output
+    dtime_script FLOAT,              -- elapsed time for script
+    hostname VARCHAR(64) NOT NULL,   -- host that executed script
+    fault SMALLINT NOT NULL,         -- fault code
+    quality SMALLINT NOT NULL,       -- bad quality flag
+    software_ver VARCHAR(16),                       -- software version
+    magicked BIGINT NOT NULL DEFAULT 0, -- magic mask applied
+    PRIMARY KEY(diff_phot_id, skycell_id),
+    KEY(fault),
+    KEY(quality),
+    FOREIGN KEY(diff_phot_id) REFERENCES diffPhotRun(diff_phot_id)
+) ENGINE=innodb DEFAULT CHARSET=latin1;
 
 -- This comment line is here to avoid empty query error.
Index: /branches/pap/ippTools/share/pxadmin_drop_tables.sql
===================================================================
--- /branches/pap/ippTools/share/pxadmin_drop_tables.sql	(revision 28483)
+++ /branches/pap/ippTools/share/pxadmin_drop_tables.sql	(revision 28484)
@@ -41,10 +41,15 @@
 DROP TABLE IF EXISTS warpSkyfile;
 DROP TABLE IF EXISTS warpMask;
+DROP TABLE IF EXISTS warpSummary;
 DROP TABLE IF EXISTS diffRun;
 DROP TABLE IF EXISTS diffInputSkyfile;
 DROP TABLE IF EXISTS diffSkyfile;
+DROP TABLE IF EXISTS diffSummary;
 DROP TABLE IF EXISTS stackRun;
 DROP TABLE IF EXISTS stackInputSkyfile;
 DROP TABLE IF EXISTS stackSumSkyfile;
+DROP TABLE IF EXISTS stackSummary;
+DROP TABLE IF EXISTS stackAssociation;
+DROP TABLE IF EXISTS stackAssociationMap;
 DROP TABLE IF EXISTS magicRun;
 DROP TABLE IF EXISTS magicInputSkyfile;
@@ -90,4 +95,6 @@
 DROP TABLE IF EXISTS warpBackgroundRun;
 DROP TABLE IF EXISTS warpBackgroundSkyfile;
+DROP TABLE IF EXISTS diffPhotRun;
+DROP TABLE IF EXISTS diffPhotSkyfile;
 
 SET FOREIGN_KEY_CHECKS=1
Index: /branches/pap/ippTools/share/stacktool_addsummary.sql
===================================================================
--- /branches/pap/ippTools/share/stacktool_addsummary.sql	(revision 28484)
+++ /branches/pap/ippTools/share/stacktool_addsummary.sql	(revision 28484)
@@ -0,0 +1,5 @@
+INSERT INTO stackSummary (sass_id,projection_cell,path_base) VALUES (
+       %lld,   -- warp_id
+       '%s',   -- projection_cell
+       '%s'    -- path_base
+)
Index: /branches/pap/ippTools/share/stacktool_associationdefine_select.sql
===================================================================
--- /branches/pap/ippTools/share/stacktool_associationdefine_select.sql	(revision 28484)
+++ /branches/pap/ippTools/share/stacktool_associationdefine_select.sql	(revision 28484)
@@ -0,0 +1,19 @@
+SELECT 
+       sass_id,
+       data_group,
+       projection_cell,
+       tess_id,
+       filter
+       FROM stackAssociation
+RIGHT JOIN (
+      SELECT 
+      	     data_group,
+	     tess_id,
+	     filter,
+	     CASE WHEN LOCATE('.',skycell_id,9) > 0 THEN
+       	     	  SUBSTRING_INDEX(skycell_id,'.',2) ELSE
+	    	  SUBSTRING_INDEX(skycell_id,'.',1) END
+       	     AS projection_cell
+       FROM stackRun 
+       WHERE stackRun.stack_id = @STACK_ID@ 
+       	     ) AS RUN USING (data_group,tess_id,filter,projection_cell) LIMIT 1
Index: /branches/pap/ippTools/share/stacktool_sassskyfile.sql
===================================================================
--- /branches/pap/ippTools/share/stacktool_sassskyfile.sql	(revision 28484)
+++ /branches/pap/ippTools/share/stacktool_sassskyfile.sql	(revision 28484)
@@ -0,0 +1,23 @@
+SELECT
+    stackAssociation.sass_id,
+    stackAssociation.projection_cell,
+    stackSumSkyfile.*,
+    stackRun.state,
+    stackRun.tess_id,
+    stackRun.skycell_id,
+    stackRun.filter,
+    stackRun.workdir,
+    stackRun.label,
+    (SELECT rawExp.camera FROM 
+        stackInputSkyfile 
+        JOIN warpRun USING(warp_id)
+        JOIN fakeRun ON warpRun.fake_id = fakeRun.fake_id
+        JOIN camRun ON camRun.cam_id = fakeRun.cam_id
+        JOIN chipRun ON camRun.chip_id  = chipRun.chip_id
+        JOIN rawExp ON chipRun.exp_id  = rawExp.exp_id
+        where stack_id = stackRun.stack_id limit 1
+    ) as camera
+FROM stackRun
+JOIN stackSumSkyfile USING(stack_id)
+JOIN stackAssociationMap USING(stack_id)
+JOIN stackAssociation USING(sass_id)
Index: /branches/pap/ippTools/share/stacktool_tosummary.sql
===================================================================
--- /branches/pap/ippTools/share/stacktool_tosummary.sql	(revision 28484)
+++ /branches/pap/ippTools/share/stacktool_tosummary.sql	(revision 28484)
@@ -0,0 +1,13 @@
+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
Index: /branches/pap/ippTools/share/warptool_addsummary.sql
===================================================================
--- /branches/pap/ippTools/share/warptool_addsummary.sql	(revision 28484)
+++ /branches/pap/ippTools/share/warptool_addsummary.sql	(revision 28484)
@@ -0,0 +1,5 @@
+INSERT INTO warpSummary (warp_id,projection_cell,path_base) VALUES(
+       %lld,   -- warp_id
+       '%s',   -- projection_cell
+       '%s'    -- path_base
+)
Index: /branches/pap/ippTools/share/warptool_tosummary.sql
===================================================================
--- /branches/pap/ippTools/share/warptool_tosummary.sql	(revision 28484)
+++ /branches/pap/ippTools/share/warptool_tosummary.sql	(revision 28484)
@@ -0,0 +1,7 @@
+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 
Index: /branches/pap/ippTools/src/Makefile.am
===================================================================
--- /branches/pap/ippTools/src/Makefile.am	(revision 28483)
+++ /branches/pap/ippTools/src/Makefile.am	(revision 28484)
@@ -26,5 +26,6 @@
 	warptool \
 	receivetool \
-	pubtool
+	pubtool \
+	diffphottool
 
 pkginclude_HEADERS = \
@@ -71,5 +72,6 @@
 	staticskytool.h \
 	warptool.h \
-	pubtool.h
+	pubtool.h \
+	diffphottool.h
 
 lib_LTLIBRARIES = libpxtools.la
@@ -272,4 +274,10 @@
     pubtoolConfig.c
 
+diffphottool_CFLAGS = $(PSLIB_CFLAGS) $(PSMODULES_CFLAGS) $(IPPDB_CFLAGS)
+diffphottool_LDADD = $(PSLIB_LIBS) $(PSMODULES_LIBS) $(IPPDB_LIBS) libpxtools.la
+diffphottool_SOURCES = \
+    diffphottool.c \
+    diffphottoolConfig.c
+
 clean-local:
 	-rm -f TAGS
Index: /branches/pap/ippTools/src/addtool.c
===================================================================
--- /branches/pap/ippTools/src/addtool.c	(revision 28483)
+++ /branches/pap/ippTools/src/addtool.c	(revision 28484)
@@ -40,4 +40,15 @@
 static bool maskedMode(pxConfig *config);
 static bool unblockMode(pxConfig *config);
+static bool addminidvodbrunMode(pxConfig *config);
+static bool updateminidvodbrunMode(pxConfig *config);
+static bool listminidvodbrunMode(pxConfig *config);
+static bool flipminidvodbrunMode(pxConfig *config);
+static bool checkminidvodbrunaddrunMode(pxConfig *config);
+static bool addminidvodbprocessedMode(pxConfig *config);
+static bool listminidvodbprocessedMode(pxConfig *config);
+static bool revertminidvodbprocessedMode(pxConfig *config);
+static bool updateminidvodbprocessedMode(pxConfig *config);
+
+
 
 # define MODECASE(caseName, func) \
@@ -69,4 +80,14 @@
         MODECASE(ADDTOOL_MODE_MASKED,               maskedMode);
         MODECASE(ADDTOOL_MODE_UNBLOCK,              unblockMode);
+        MODECASE(ADDTOOL_MODE_ADDMINIDVODBRUN,      addminidvodbrunMode);
+        MODECASE(ADDTOOL_MODE_UPDATEMINIDVODBRUN,   updateminidvodbrunMode);
+        MODECASE(ADDTOOL_MODE_LISTMINIDVODBRUN,     listminidvodbrunMode);
+        MODECASE(ADDTOOL_MODE_FLIPMINIDVODBRUN,     flipminidvodbrunMode);
+        MODECASE(ADDTOOL_MODE_CHECKMINIDVODBRUNADDRUN, checkminidvodbrunaddrunMode);
+        MODECASE(ADDTOOL_MODE_ADDMINIDVODBPROCESSED,addminidvodbprocessedMode);
+        MODECASE(ADDTOOL_MODE_LISTMINIDVODBPROCESSED,listminidvodbprocessedMode);
+        MODECASE(ADDTOOL_MODE_REVERTMINIDVODBPROCESSED,revertminidvodbprocessedMode);
+        MODECASE(ADDTOOL_MODE_UPDATEMINIDVODBPROCESSED,updateminidvodbprocessedMode);
+
         default:
             psAbort("invalid option (this should not happen)");
@@ -102,4 +123,5 @@
     PXOPT_COPY_STR(config->args, where,  "-reduction", "camRun.reduction", "==");
 
+
     if (!psListLength(where->list)) {
         psFree(where);
@@ -114,8 +136,13 @@
     PXOPT_LOOKUP_STR(reduction,   config->args, "-set_reduction", false, false);
     PXOPT_LOOKUP_STR(note,        config->args, "-set_note", false, false);
+    PXOPT_LOOKUP_STR(minidvodb_name,  config->args, "-set_minidvodb_name", false, false);
+    PXOPT_LOOKUP_STR(minidvodb_group, config->args, "-set_minidvodb_group", false, false);
     PXOPT_LOOKUP_BOOL(image_only, config->args, "-image_only", false);
+    PXOPT_LOOKUP_BOOL(minidvodb,  config->args, "-set_minidvodb", false);
     PXOPT_LOOKUP_BOOL(destreaked, config->args, "-destreaked", false);
     PXOPT_LOOKUP_BOOL(pretend,    config->args, "-pretend", false);
     PXOPT_LOOKUP_BOOL(simple,     config->args, "-simple", false);
+
+
 
     // find the cam_id of all the exposures that we want to queue up.
@@ -245,8 +272,11 @@
                                dvodb       ? dvodb     : row->dvodb,
                                note        ? note      : NULL,
-                               image_only
+                               image_only,
+                               minidvodb,
+                               minidvodb_group,
+                               minidvodb_name
         )) {
             if (!psDBRollback(config->dbh)) {
-                psError(PS_ERR_UNKNOWN, false, "database error");
+                psError(PS_ERR_UNKNOWN, false, "database error sfg");
             }
             psError(PS_ERR_UNKNOWN, false,
@@ -291,5 +321,5 @@
 
     // pxUpdateRun gets parameters from config->args and runs the update query
-    bool result = pxUpdateRun(config, where, &query, "addRun", "add_id", 
+    bool result = pxUpdateRun(config, where, &query, "addRun", "add_id",
         "addProcessedExp", false);
 
@@ -374,4 +404,6 @@
     PXOPT_LOOKUP_STR(path_base,     config->args, "-path_base", false, false);
     PXOPT_LOOKUP_F32(dtime_addstar, config->args, "-dtime_addstar", false, false);
+    PXOPT_LOOKUP_STR(dvodb_path, config->args, "-dvodb_path", false, false);
+    PXOPT_LOOKUP_STR(minidvodb_name, config->args, "-minidvodb_name", false, false);
     PXOPT_LOOKUP_S16(fault,         config->args, "-fault", false, false);
 
@@ -399,4 +431,5 @@
         return false;
     }
+
     psFree(query);
 
@@ -424,4 +457,5 @@
         dtime_addstar,
         path_base,
+        dvodb_path,
         fault
         );
@@ -437,4 +471,22 @@
         return false;
     }
+
+    //if there is a minidvodb_name, set it in addRun (it's not known until it is processed)
+    if (minidvodb_name) {
+      psString setName = NULL;
+      psStringAppend (&setName, "UPDATE addRun set minidvodb_name = '%s' where add_id = %" PRId64, minidvodb_name, row->add_id);
+      if (!p_psDBRunQuery(config->dbh, setName)) {
+        if (!psDBRollback(config->dbh)) {
+          psError(PS_ERR_UNKNOWN, false, "database error");
+        }
+        psError(PS_ERR_UNKNOWN, false, "database error");
+
+        return false;
+      }
+    }
+
+
+
+
 
     // since there is only one exp per 'new' set addRun.state = 'full'
@@ -522,5 +574,5 @@
 
     if (!p_psDBRunQuery(config->dbh, query)) {
-        psError(PS_ERR_UNKNOWN, false, "database error");
+      psError(PS_ERR_UNKNOWN, false, "database error");
         psFree(query);
         return false;
@@ -576,6 +628,10 @@
 
     {
-        psString query = pxDataGet("addtool_reset_faulted_runs.sql");
+        psString query = pxDataGet("addtool_revertprocessedexp.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);
@@ -602,36 +658,4 @@
         psFree(query);
     }
-
-    {
-        psString query = pxDataGet("addtool_revertprocessedexp.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);
 
@@ -733,2 +757,818 @@
     return true;
 }
+
+static bool addminidvodbrunMode(pxConfig *config)
+{
+  PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+  // required
+
+  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
+  PXOPT_LOOKUP_STR(minidvodb_name, config->args, "-set_minidvodb_name", false, false);
+  PXOPT_LOOKUP_STR(minidvodb_path, config->args, "-set_minidvodb_path", false, false);
+  PXOPT_LOOKUP_STR(state, config->args, "-set_state", false, false);
+
+  if (!psDBTransaction(config->dbh)) {
+    psError(PS_ERR_UNKNOWN, false, "database error");
+    return false;
+  }
+
+  psString minidvodbpath = "NULL";
+
+  // I don't know how to get around the complaints of minidvodb_path can't be null. this 'fixes' it, but someone smarter can fix it properly.
+  if (minidvodb_path) {
+    minidvodbpath = minidvodb_path;
+  }
+
+  if (!minidvodbRunInsert(config->dbh,
+            0, // job_id
+            minidvodb_name,
+            minidvodb_group,
+            minidvodbpath,
+            mergedvodb_path,
+            "new",
+            0
+            )) {
+    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 minidvodb_id = psDBLastInsertID(config->dbh);
+  printf("%" PRId64 "\n", minidvodb_id);
+
+
+  if (!minidvodb_name) {
+    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);
+  }
+
+
+  psString query = NULL;
+
+  psStringAppend(&query, "UPDATE minidvodbRun SET minidvodb_path = '%s', minidvodb_name = '%s' where minidvodb_id = %" PRIu64";", minidvodb_path, minidvodb_name, minidvodb_id);
+
+  if (!p_psDBRunQuery(config->dbh, query)) {
+    psError(PS_ERR_UNKNOWN, false, "database error");
+    psFree(query);
+    return false;
+  }
+
+  if (!psDBCommit(config->dbh)) {
+    psError(PS_ERR_UNKNOWN, false, "database error");
+    return false;
+  }
+
+  return true;
+}
+
+static bool updateminidvodbrunMode(pxConfig *config) {
+  PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-minidvodb_id",     "minidvodb_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-minidvodb_name",     "minidvodb_name", "==");
+    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", "==");
+
+    PXOPT_LOOKUP_STR(minidvodb_name,  config->args, "-set_minidvodb_name", false, false);
+    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);
+
+
+    if (!psListLength(where->list)) {
+      psFree(where);
+      psError(PXTOOLS_ERR_CONFIG, false, "search parameters are required");
+      return false;
+    }
+
+    psString query = psStringCopy("UPDATE minidvodbRun SET ");
+    int cnt = 0;
+    psString comma = ",";
+    if (minidvodb_name) {
+      psStringAppend(&query, " minidvodb_name = '%s'", minidvodb_name);
+      cnt++;
+    }
+
+    if (minidvodb_path) {
+      if (cnt) {
+          psStringAppend(&query, "%s", comma);
+      }
+
+      psStringAppend(&query, " minidvodb_path = '%s'", minidvodb_path);
+      cnt++;
+    }
+
+    if (state) {
+      if (cnt) {
+        psStringAppend(&query, "%s", comma);
+      }
+      psStringAppend(&query, " state = '%s'", state);
+      cnt++;
+    }
+
+    if (mergedvodb_path) {
+      if (cnt) {
+        psStringAppend(&query, "%s", comma);
+      }
+      psStringAppend(&query, " mergedvodb_path = '%s'", mergedvodb_path);
+      cnt++;
+    }
+
+    if (minidvodb_group) {
+      if (cnt) {
+        psStringAppend(&query, "%s", comma);
+      }
+      psStringAppend(&query, " minidvodb_group = '%s'", minidvodb_group);
+      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 flipminidvodbrunMode(pxConfig *config) {
+  PS_ASSERT_PTR_NON_NULL(config, false);
+
+  psMetadata *where = psMetadataAlloc();
+  PXOPT_LOOKUP_STR(minidvodb_group,  config->args, "-minidvodb_group",true, false);
+  PXOPT_COPY_STR(config->args, where, "-minidvodb_group",     "minidvodb_group", "==");
+
+//this flips the new - > active
+// and the active - > waiting in one action
+
+
+// the first query looks to find things that are new and where all the fields are filled (ie, ready to be flipped to active)
+
+// start a transaction eraly so it will contain any row level locks
+  if (!psDBTransaction(config->dbh)) {
+    psError(PS_ERR_UNKNOWN, false, "database error");
+    return false;
+  }
+
+  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 firstwhereClause = psDBGenerateWhereConditionSQL(where, NULL);
+  psStringAppend(&firstquery, " AND %s", firstwhereClause);
+
+
+
+  if (!p_psDBRunQuery(config->dbh, firstquery)) {
+    psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+    if (!psDBRollback(config->dbh)) {
+      psError(PS_ERR_UNKNOWN, false, "database error");
+    }
+    return false;
+  }
+  psFree(firstquery);
+
+ //we don't care what the stuff is that is found, just that there is stuff. This is a check to see that there is something in the 'new' state, before flipping (so that if there is nothing in new, it won't flip the active to waiting.  the flipminidvo is just to make it easy to flip from new -> active.
+
+  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;
+  }
+
+
+  //ok, there's something, so flip active -> waiting
+
+  psString query = psStringCopy("UPDATE minidvodbRun SET state = 'waiting' WHERE state = 'active' ");
+
+  psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+  psStringAppend(&query, " AND %s", whereClause);
+
+
+  if (!p_psDBRunQuery(config->dbh, query)) {
+    psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+    if (!psDBRollback(config->dbh)) {
+      psError(PS_ERR_UNKNOWN, false, "database error");
+    }
+    return false;
+  }
+
+
+  //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 ");
+  psStringAppend(&query2, " AND minidvodb_group = '%s' limit 1;", minidvodb_group);
+  if (!p_psDBRunQuery(config->dbh, query2)) {
+    psError(PS_ERR_UNKNOWN, false, "database error");
+    if (!psDBRollback(config->dbh)) {
+      psError(PS_ERR_UNKNOWN, false, "database error");
+    }
+    psFree(query);
+    return false;
+  }
+
+
+  if (!psDBCommit(config->dbh)) {
+    psError(PS_ERR_UNKNOWN, false, "database error");
+    if (!psDBRollback(config->dbh)) {
+      psError(PS_ERR_UNKNOWN, false, "database error");
+    }
+    psFree(query);
+  return false;
+  }
+ psFree(query2);
+ psFree(where);
+
+ return true;
+
+}
+
+
+static bool checkminidvodbrunaddrunMode(pxConfig *config) {
+  PS_ASSERT_PTR_NON_NULL(config, false);
+  psMetadata *where = psMetadataAlloc();
+
+  //this checks to see if a minidvod_group/name is has completed addRun processing
+  PXOPT_COPY_STR(config->args, where, "-minidvodb_group", "minidvodbRun.minidvodb_group", "==");
+  PXOPT_COPY_STR(config->args, where, "-minidvodb_name", "minidvodbRun.minidvodb_name", "==");
+  PXOPT_COPY_STR(config->args, where, "-state", "minidvodbRun.state", "==");
+
+
+  PXOPT_LOOKUP_U64(limit,      config->args, "-limit", false, false);
+  PXOPT_LOOKUP_BOOL(all_addrun_states, config->args, "-all_addrun_states", false);
+  //this doesn't care what state the addRun is in (useful for counting addRuns)
+  PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+  if (!psListLength(where->list)) {
+        psFree(where);
+        psError(PXTOOLS_ERR_CONFIG, false, "search parameters are required");
+        return false;
+  }
+
+  psString query = pxDataGet("addtool_checkminidvodbaddrun.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);
+  }
+
+  if (!all_addrun_states) {
+    psStringAppend(&query, " AND (cnt2 = cnt) ");
+  }
+
+  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;
+  }
+
+  if (!ippdbPrintMetadatas(stdout, output, "minidvodbRun", !simple)) {
+    psError(PS_ERR_UNKNOWN, false, "failed to print array");
+    psFree(output);
+    return false;
+  }
+
+  psFree(output);
+
+  return true;
+}
+
+
+
+static bool listminidvodbrunMode(pxConfig *config) {
+    PS_ASSERT_PTR_NON_NULL(config, false);
+    psMetadata *where = psMetadataAlloc();
+
+    PXOPT_COPY_STR(config->args, where, "-minidvodb_name", "minidvodbRun.minidvodb_name", "LIKE");
+    PXOPT_COPY_STR(config->args, where, "-minidvodb_group", "minidvodbRun.minidvodb_group", "==");
+    PXOPT_COPY_S64(config->args, where, "-minidvodb_id", "minidvodbRun.minidvodb_id", "==");
+
+    PXOPT_COPY_STR(config->args, where, "-state", "minidvodbRun.state", "==");
+    PXOPT_LOOKUP_U64(limit,      config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+
+    if (!psListLength(where->list)) {
+      psFree(where);
+      psError(PXTOOLS_ERR_CONFIG, false, "search parameters are required");
+      return false;
+    }
+
+    psString query = pxDataGet("addtool_find_minidvodbrun.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);
+    }
+
+    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;
+    }
+
+    if (!ippdbPrintMetadatas(stdout, output, "minidvodbRun",  !simple)) {
+      psError(PS_ERR_UNKNOWN, false, "failed to print array");
+      psFree(output);
+      return false;
+    }
+
+    psFree(output);
+
+return true;
+}
+
+static bool addminidvodbprocessedMode(pxConfig *config) {
+
+  PS_ASSERT_PTR_NON_NULL(config, false);
+
+    // 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_TIME(epoch, config->args, "-epoch", false, false);
+  PXOPT_LOOKUP_S16(fault,         config->args, "-fault", false, false);
+    //generate restrictions
+  psMetadata *where = psMetadataAlloc();
+  PXOPT_COPY_S64(config->args, where, "-minidvodb_id",   "minidvodbRun.minidvodb_id",   "==");
+
+
+  if (!psDBTransaction(config->dbh)) {
+    psError(PS_ERR_UNKNOWN, false, "database error");
+    return false;
+  }
+
+
+  psString query = pxDataGet("addtool_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);
+
+  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;
+  }
+
+  minidvodbRunRow *pendingRow = minidvodbRunObjectFromMetadata(output->data[0]);
+  psFree(output);
+  minidvodbProcessedRow *row = minidvodbProcessedRowAlloc(
+               pendingRow->minidvodb_id,
+               merge_order,
+               dtime_resort,
+               dtime_relphot,
+               dtime_merge,
+               epoch,
+               mergedvodb_path,
+               fault
+               );
+
+  if (!minidvodbProcessedInsertObject(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 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);
+
+  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 listminidvodbprocessedMode(pxConfig *config) {
+  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_LOOKUP_U64(limit, config->args, "-limit", false, false);
+  PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", 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("addtool_find_minidvodbprocessed.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 minidvodbProcessed.fault != 0");
+    }
+    if (where->list && !faulted) {
+        // don't list faulted rows
+        psStringAppend(&query, " %s", " AND minidvodbProcessed.fault = 0");
+    }
+    if (!where->list && faulted) {
+        // list only faulted rows
+        psStringAppend(&query, " %s", " WHERE minidvodbProcessed.fault != 0");
+    }
+    if (!where->list && !faulted) {
+        // don't list faulted rows
+        psStringAppend(&query, " %s", " WHERE minidvodbProcessed.fault = 0");
+    }
+    psFree(where);
+
+    // order by epoch
+    psStringAppend(&query, " ORDER BY minidvodb_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, "minidvodbProcessed", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+return true;
+}
+
+
+
+static bool revertminidvodbprocessedMode(pxConfig *config) {
+  psMetadata *where = psMetadataAlloc();
+  PS_ASSERT_PTR_NON_NULL(config, false);
+  PXOPT_COPY_S64(config->args, where, "-minidvodb_id", "minidvodbProcessed.minidvodb_id", "==");
+  PXOPT_COPY_STR(config->args, where, "-minidvodb_group", "addRun.minidvodb_group", "==");
+
+  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("addtool_revertminidvodbprocessed.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 updateminidvodbprocessedMode(pxConfig *config) {
+  PS_ASSERT_PTR_NON_NULL(config, false);
+  psMetadata *where = psMetadataAlloc();
+
+  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_COPY_S64(config->args, where, "-minidvodb_id",     "minidvodbProcessed.minidvodb_id", "==");
+  PXOPT_COPY_STR(config->args, where, "-minidvodb_name",     "minidvodbRun.minidvodb_name", "==");
+
+
+  if (!psListLength(where->list)) {
+    psFree(where);
+    psError(PXTOOLS_ERR_CONFIG, false, "search parameters are required");
+    return false;
+  }
+
+  psString query = psStringCopy("UPDATE minidvodbProcessed JOIN minidvodbRun USING (minidvodb_id) SET ");
+  int cnt = 0;
+  psString comma = ",";
+  if (fault) {
+    psStringAppend(&query, " fault = %d", fault);
+  cnt++;
+  }
+
+  if (merge_order) {
+    if (cnt) {
+      psStringAppend(&query, "%s", comma);
+    }
+
+    psStringAppend(&query, " merge_order = %" PRId64, merge_order);
+    cnt++;
+  }
+
+  if (dtime_relphot) {
+    if (cnt) {
+      psStringAppend(&query, "%s", comma);
+    }
+    psStringAppend(&query, " dtime_relphot = %f", dtime_relphot);
+    cnt++;
+  }
+
+  if (dtime_resort) {
+    if (cnt) {
+      psStringAppend(&query, "%s", comma);
+    }
+    psStringAppend(&query, " dtime_resort = %f", dtime_resort);
+    cnt++;
+  }
+
+  if (dtime_merge) {
+    if (cnt) {
+      psStringAppend(&query, "%s", comma);
+    }
+    psStringAppend(&query, " dtime_merge = %f", dtime_merge);
+    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;
+}
+
+
Index: /branches/pap/ippTools/src/addtool.h
===================================================================
--- /branches/pap/ippTools/src/addtool.h	(revision 28483)
+++ /branches/pap/ippTools/src/addtool.h	(revision 28484)
@@ -39,5 +39,14 @@
     ADDTOOL_MODE_DONECLEANUP,
     ADDTOOL_MODE_EXPORTRUN,
-    ADDTOOL_MODE_IMPORTRUN
+    ADDTOOL_MODE_IMPORTRUN,
+    ADDTOOL_MODE_ADDMINIDVODBRUN,
+    ADDTOOL_MODE_UPDATEMINIDVODBRUN,
+    ADDTOOL_MODE_LISTMINIDVODBRUN,
+    ADDTOOL_MODE_FLIPMINIDVODBRUN,
+    ADDTOOL_MODE_CHECKMINIDVODBRUNADDRUN,
+    ADDTOOL_MODE_ADDMINIDVODBPROCESSED,
+    ADDTOOL_MODE_LISTMINIDVODBPROCESSED,
+    ADDTOOL_MODE_REVERTMINIDVODBPROCESSED,
+    ADDTOOL_MODE_UPDATEMINIDVODBPROCESSED
 } addtoolMode;
 
Index: /branches/pap/ippTools/src/addtoolConfig.c
===================================================================
--- /branches/pap/ippTools/src/addtoolConfig.c	(revision 28483)
+++ /branches/pap/ippTools/src/addtoolConfig.c	(revision 28484)
@@ -63,5 +63,7 @@
     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);
-
+    psMetadataAddBool(definebyqueryArgs, PS_LIST_TAIL, "-set_minidvodb",            0, "use minidvodb", false);
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-set_minidvodb_group", 0,   "define minidvodb_group", NULL);
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-set_minidvodb_name", 0,   "define minidvodb_bname", NULL);
     psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-set_data_group", 0,   "define new data_group", NULL);
     psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-set_dist_group", 0,   "define new dist_group", NULL);
@@ -84,4 +86,5 @@
     psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-set_dist_group", 0,   "define new dist_group", NULL);
     psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-set_note", 0,         "define new note", NULL);
+
     // -pendingexp
     psMetadata *pendingexpArgs = psMetadataAlloc();
@@ -98,4 +101,6 @@
     psMetadataAddS64(addprocessedexpArgs, PS_LIST_TAIL, "-add_id", 0,            "define addtool ID (required)", 0);
     psMetadataAddStr(addprocessedexpArgs, PS_LIST_TAIL, "-path_base", 0,            "define base output location", NULL);
+     psMetadataAddStr(addprocessedexpArgs, PS_LIST_TAIL, "-dvodb_path", 0,            "define base output location", NULL);
+      psMetadataAddStr(addprocessedexpArgs, PS_LIST_TAIL, "-minidvodb_name", 0,            "define minidvodb_name", NULL);
     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);
@@ -142,4 +147,79 @@
     psMetadataAddStr(unblockArgs, PS_LIST_TAIL, "-label",  0,            "name of a label to unmask (required)", NULL);
 
+    // -addminidvodbruns
+    psMetadata *addminidvodbrunArgs = psMetadataAlloc();
+    psMetadataAddStr(addminidvodbrunArgs, PS_LIST_TAIL, "-set_minidvodb_name",        0, "define minidvodb_name", NULL);
+    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);
+
+    // -updateminidvodbruns
+    psMetadata *updateminidvodbrunArgs = psMetadataAlloc();
+    psMetadataAddU64(updateminidvodbrunArgs, PS_LIST_TAIL, "-minidvodb_id",        0, "search by minidvodb_id ", 0);
+    psMetadataAddStr(updateminidvodbrunArgs, PS_LIST_TAIL, "-minidvodb_group",        0, "search by minidvodb_name (LIKE)", NULL);
+    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);
+    
+    // -listminidvodbrunArgs
+    psMetadata *listminidvodbrunArgs = psMetadataAlloc();
+    psMetadataAddU64(listminidvodbrunArgs, PS_LIST_TAIL, "-minidvodb_id",        0, "search by minidvodb_id", 0);
+    psMetadataAddStr(listminidvodbrunArgs, PS_LIST_TAIL, "-minidvodb_name",        0, "search by minidvodb_name (LIKE)", NULL);
+    psMetadataAddStr(listminidvodbrunArgs, PS_LIST_TAIL, "-minidvodb_group",     0,    "search by minidvodb.minidvodb_group", NULL);
+    psMetadataAddStr(listminidvodbrunArgs, PS_LIST_TAIL, "-state",        0, "search by state", NULL);
+    psMetadataAddU64(listminidvodbrunArgs, PS_LIST_TAIL, "-limit",        0, "limit to N items", 0);
+    psMetadataAddBool(listminidvodbrunArgs, PS_LIST_TAIL, "-simple",        0, "simple output", false);
+    
+    //psMetadataAddBool(listminidvodbrunArgs, PS_LIST_TAIL, "-finished_addrun",        0, "limit to minidvodbs with completed addRuns (none in new state)", false);
+    psMetadata *flipminidvodbrunArgs = psMetadataAlloc();
+    psMetadataAddStr(flipminidvodbrunArgs, PS_LIST_TAIL, "-minidvodb_group",     0,    "for the supplied minidvodb_group (required): flip the current 'new' to 'active', the current 'active' to 'waiting'", NULL);
+    
+    psMetadata *checkminidvodbrunaddrunArgs = psMetadataAlloc();
+    psMetadataAddStr(checkminidvodbrunaddrunArgs, PS_LIST_TAIL, "-minidvodb_group",     0,    "for the supplied minidvodb_group (required): check if the addRun stage is complete (all in addRun.state = full) ", NULL);
+    psMetadataAddStr(checkminidvodbrunaddrunArgs, PS_LIST_TAIL, "-minidvodb_name",     0,    "for the supplied minidvodb_name: check if the addRun stage is complete (all in addRun.state = full) ", NULL);
+    psMetadataAddStr(checkminidvodbrunaddrunArgs, PS_LIST_TAIL, "-state",     0,    "limit by minidvodbRun state ", NULL);
+    psMetadataAddU64(checkminidvodbrunaddrunArgs, PS_LIST_TAIL, "-limit",        0, "limit to N items", 0);
+    psMetadataAddBool(checkminidvodbrunaddrunArgs, PS_LIST_TAIL, "-simple",        0, "simple output", false);
+    psMetadataAddBool(checkminidvodbrunaddrunArgs, PS_LIST_TAIL, "-all_addrun_states",        0, "list all minidvodbRun.minidvodb_names, not just ones that have complete addRuns", false);
+
+    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_merge",    0,    "define elapsed time for DVO merge (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);
+
+    psMetadata *listminidvodbprocessedArgs = psMetadataAlloc();
+    psMetadataAddU64(listminidvodbprocessedArgs, PS_LIST_TAIL, "-minidvodb_id",        0, "search by minidvodb_id", 0);
+    psMetadataAddStr(listminidvodbprocessedArgs, PS_LIST_TAIL, "-minidvodb_name",        0, "search by minidvodb_name", NULL);
+    psMetadataAddStr(listminidvodbprocessedArgs, PS_LIST_TAIL, "-minidvodb_group",        0, "search by minidvodb.minidvodb_group", NULL);
+    psMetadataAddU64(listminidvodbprocessedArgs, PS_LIST_TAIL, "-limit",        0, "limit to N items", 0);
+    psMetadataAddBool(listminidvodbprocessedArgs, PS_LIST_TAIL, "-simple",        0, "simple output", false);
+    psMetadataAddBool(listminidvodbprocessedArgs, PS_LIST_TAIL, "-faulted",        0, "limit to faulted state", false);
+
+    psMetadata *revertminidvodbprocessedArgs = psMetadataAlloc();
+    psMetadataAddU64(revertminidvodbprocessedArgs, PS_LIST_TAIL, "-minidvodb_id",        0, "search by minidvodb_id", 0);
+    psMetadataAddStr(revertminidvodbprocessedArgs, PS_LIST_TAIL, "-minidvodb_group",        0, "search by addRun.minidvodb_group", NULL);
+
+    psMetadata *updateminidvodbprocessedArgs = psMetadataAlloc();
+    psMetadataAddU64(updateminidvodbprocessedArgs, PS_LIST_TAIL, "-minidvodb_id",        0, "search by minidvodb_id", 0);
+    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_merge",    0,    "define elapsed time for DVO merge (seconds)", 0);
+    
     psMetadata *argSets = psMetadataAlloc();
     psMetadata *modes = psMetadataAlloc();
@@ -154,5 +234,16 @@
     PXOPT_ADD_MODE("-block",                "set a label block",                    ADDTOOL_MODE_BLOCK,         blockArgs);
     PXOPT_ADD_MODE("-masked",               "show blocked labels",                  ADDTOOL_MODE_MASKED,        maskedArgs);
-    PXOPT_ADD_MODE("-unblock",              "remove a label block",                 ADDTOOL_MODE_UNBLOCK,       unblockArgs);
+    PXOPT_ADD_MODE("-addminidvodbrun",      "create minidvodbs ",                   ADDTOOL_MODE_ADDMINIDVODBRUN, addminidvodbrunArgs);
+    PXOPT_ADD_MODE("-updateminidvodbrun",   "change minidvodb properties",          ADDTOOL_MODE_UPDATEMINIDVODBRUN,     updateminidvodbrunArgs);
+    PXOPT_ADD_MODE("-listminidvodbrun",     "list minidvodbs",                      ADDTOOL_MODE_LISTMINIDVODBRUN,       listminidvodbrunArgs);
+    PXOPT_ADD_MODE("-flipminidvodbrun",     "flip minidvodbs",                      ADDTOOL_MODE_FLIPMINIDVODBRUN,       flipminidvodbrunArgs);
+    PXOPT_ADD_MODE("-checkminidvodbrunaddrun", "check minidvodbs to see if addRuns are completed", ADDTOOL_MODE_CHECKMINIDVODBRUNADDRUN,       checkminidvodbrunaddrunArgs);
+    PXOPT_ADD_MODE("-addminidvodbprocessed","add a processed minidvodb",            ADDTOOL_MODE_ADDMINIDVODBPROCESSED,  addminidvodbprocessedArgs);
+    PXOPT_ADD_MODE("-listminidvodbprocessed","list processed minidvodbs",           ADDTOOL_MODE_LISTMINIDVODBPROCESSED, listminidvodbprocessedArgs);
+    PXOPT_ADD_MODE("-revertminidvodbprocessed","revert processed minidvobs",        ADDTOOL_MODE_REVERTMINIDVODBPROCESSED,     revertminidvodbprocessedArgs);
+    PXOPT_ADD_MODE("-updateminidvodbprocessed","change processed minidvodb properties",ADDTOOL_MODE_UPDATEMINIDVODBPROCESSED,  updateminidvodbprocessedArgs);
+ 
+ 
+
 
     if (!pxGetOptions(stderr, argc, argv, config, modes, argSets)) {
Index: /branches/pap/ippTools/src/camtool.c
===================================================================
--- /branches/pap/ippTools/src/camtool.c	(revision 28483)
+++ /branches/pap/ippTools/src/camtool.c	(revision 28484)
@@ -130,4 +130,8 @@
     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("camtool_find_chip_id.sql");
@@ -161,4 +165,15 @@
     if (!psArrayLength(output)) {
         psTrace("camtool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    if (pretend) {
+        // 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;
@@ -292,5 +307,5 @@
     PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
 
-    psString query = pxDataGet("camtool_find_pendingexp.sql");
+    psString query = pxDataGet("camtool_pendingexp.sql");
     if (!query) {
         psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
@@ -305,4 +320,6 @@
     }
     psFree(where);
+
+    psStringAppend(&query, "\nORDER BY priority DESC, cam_id");
 
     // treat limit == 0 as "no limit"
@@ -503,5 +520,5 @@
 
 /*     psTrace("czw.test",1,"Received versions: pslib %s psmodules %s psphot %s psastro %s ppstats %s ppImage %s streaks %s\n", */
-/* 	    ver_pslib,ver_psmodules,ver_psphot,ver_psastro,ver_ppstats,ver_ppimage,ver_streaks); */
+/*          ver_pslib,ver_psmodules,ver_psphot,ver_psastro,ver_ppstats,ver_ppimage,ver_streaks); */
     psString software_ver = NULL;
     if ((ver_pslib)&&(ver_psmodules)) {
@@ -531,5 +548,5 @@
     PXOPT_COPY_S64(config->args, where, "-cam_id",   "camRun.cam_id",   "==");
 
-    psString query = pxDataGet("camtool_find_pendingexp.sql");
+    psString query = pxDataGet("camtool_addprocessedexp.sql");
     if (!query) {
         psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
@@ -637,16 +654,16 @@
         path_base,
         fault,
-	software_ver,
-	maskfrac_ref_npix,
-	maskfrac_ref_static,
-	maskfrac_ref_dynamic,
-	maskfrac_ref_magic,
-	maskfrac_ref_advisory,
-	maskfrac_max_npix,
-	maskfrac_max_static,
-	maskfrac_max_dynamic,
-	maskfrac_max_magic,
-	maskfrac_max_advisory,
-	quality
+        software_ver,
+        maskfrac_ref_npix,
+        maskfrac_ref_static,
+        maskfrac_ref_dynamic,
+        maskfrac_ref_magic,
+        maskfrac_ref_advisory,
+        maskfrac_max_npix,
+        maskfrac_max_static,
+        maskfrac_max_dynamic,
+        maskfrac_max_magic,
+        maskfrac_max_advisory,
+        quality
         );
 
@@ -677,8 +694,8 @@
     if (!pxSetRunSoftware(config, "camRun", "cam_id", cam_id, software_ver)) {
       if (!psDBRollback(config->dbh)) {
-	psError(PS_ERR_UNKNOWN, false, "database error");
+        psError(PS_ERR_UNKNOWN, false, "database error");
       }
       psError(PS_ERR_UNKNOWN, false, "failed to set camRun.software_ver for cam_id: %" PRId64,
-	      cam_id);
+              cam_id);
       psFree(output);
       return(false);
@@ -687,15 +704,15 @@
     if (maskfrac_ref_npix) {
       if (!pxCamSetRunMaskfrac(config, "camRun", "cam_id",cam_id,
-			       (float) maskfrac_ref_npix, maskfrac_ref_static,
-			       maskfrac_ref_dynamic, maskfrac_ref_magic, maskfrac_ref_advisory,
-			       (float) maskfrac_max_npix, maskfrac_max_static,
-			       maskfrac_max_dynamic, maskfrac_max_magic, maskfrac_max_advisory)) {
-	if (!psDBRollback(config->dbh)) {
-	  psError(PS_ERR_UNKNOWN, false, "database error");
-	}
-	psError(PS_ERR_UNKNOWN, false, "failed to set camRun.maskstats for cam_id: %" PRId64,
-		cam_id);
-	psFree(output);
-	return(false);
+                               (float) maskfrac_ref_npix, maskfrac_ref_static,
+                               maskfrac_ref_dynamic, maskfrac_ref_magic, maskfrac_ref_advisory,
+                               (float) maskfrac_max_npix, maskfrac_max_static,
+                               maskfrac_max_dynamic, maskfrac_max_magic, maskfrac_max_advisory)) {
+        if (!psDBRollback(config->dbh)) {
+          psError(PS_ERR_UNKNOWN, false, "database error");
+        }
+        psError(PS_ERR_UNKNOWN, false, "failed to set camRun.maskstats for cam_id: %" PRId64,
+                cam_id);
+        psFree(output);
+        return(false);
       }
     }
@@ -709,10 +726,11 @@
         return false;
     }
-    
+
     psFree(row);
 
     // EAM:  NULL for end_stage means go as far as possible
     // Also, we can run fake even if tess_id is not defined
-    if (pendingRow->end_stage && psStrcasestr(pendingRow->end_stage, "cam")) {
+    // but stop if quality is non-zero.
+    if ((quality > 0) || (pendingRow->end_stage && psStrcasestr(pendingRow->end_stage, "cam"))) {
         psFree(pendingRow);
         if (!psDBCommit(config->dbh)) {
@@ -893,6 +911,10 @@
 
     {
-        psString query = pxDataGet("camtool_reset_faulted_runs.sql");
+        psString query = pxDataGet("camtool_revertprocessedexp.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);
@@ -919,36 +941,4 @@
         psFree(query);
     }
-
-    {
-        psString query = pxDataGet("camtool_revertprocessedexp.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);
 
Index: /branches/pap/ippTools/src/camtoolConfig.c
===================================================================
--- /branches/pap/ippTools/src/camtoolConfig.c	(revision 28483)
+++ /branches/pap/ippTools/src/camtoolConfig.c	(revision 28484)
@@ -65,4 +65,6 @@
     psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-set_dist_group",     0, "define dist group", NULL);
     psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-set_note",           0, "define note", NULL);
+    psMetadataAddBool(definebyqueryArgs, PS_LIST_TAIL, "-pretend",           0, "do not actual modify the database", false);
+    psMetadataAddBool(definebyqueryArgs, PS_LIST_TAIL, "-simple",            0, "use the simple output format", false);
 
     // -updaterun
@@ -83,4 +85,5 @@
     psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-set_dist_group", 0,   "define new dist_group", NULL);
     psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-set_note", 0,         "define new note", NULL);
+
     // -pendingexp
     psMetadata *pendingexpArgs = psMetadataAlloc();
Index: /branches/pap/ippTools/src/diffphottool.c
===================================================================
--- /branches/pap/ippTools/src/diffphottool.c	(revision 28484)
+++ /branches/pap/ippTools/src/diffphottool.c	(revision 28484)
@@ -0,0 +1,553 @@
+/*
+ * diffphottool.c
+ *
+ * Copyright (C) 2007-2010  Joshua Hoblitt, Paul Price
+ *
+ * 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 HAVB_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <math.h>
+#include <ippdb.h>
+
+#include "pxtools.h"
+#include "diffphottool.h"
+
+static bool definerunMode(pxConfig *config);
+static bool updaterunMode(pxConfig *config);
+static bool inputMode(pxConfig *config);
+static bool pendingMode(pxConfig *config);
+static bool doneMode(pxConfig *config);
+static bool advanceMode(pxConfig *config);
+static bool revertMode(pxConfig *config);
+static bool dataMode(pxConfig *config);
+
+# define MODECASE(caseName, func) \
+    case caseName: \
+    if (!func(config)) { \
+        goto FAIL; \
+    } \
+    break;
+
+int main(int argc, char **argv)
+{
+    psLibInit(NULL);
+
+    pxConfig *config = diffphottoolConfig(NULL, argc, argv);
+    if (!config) {
+        psError(PXTOOLS_ERR_CONFIG, false, "failed to configure");
+        goto FAIL;
+    }
+
+    switch (config->mode) {
+        MODECASE(DIFFPHOTTOOL_MODE_DEFINERUN, definerunMode);
+        MODECASE(DIFFPHOTTOOL_MODE_UPDATERUN, updaterunMode);
+        MODECASE(DIFFPHOTTOOL_MODE_INPUT,     inputMode);
+        MODECASE(DIFFPHOTTOOL_MODE_PENDING,   pendingMode);
+        MODECASE(DIFFPHOTTOOL_MODE_DONE,      doneMode);
+        MODECASE(DIFFPHOTTOOL_MODE_ADVANCE,   advanceMode);
+        MODECASE(DIFFPHOTTOOL_MODE_REVERT,    revertMode);
+        MODECASE(DIFFPHOTTOOL_MODE_DATA,      dataMode);
+
+        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 definerunMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    // Options
+    PXOPT_LOOKUP_STR(set_workdir, config->args, "-set_workdir", true, 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(set_reduction, config->args, "-set_reduction", false, false);
+    PXOPT_LOOKUP_STR(set_note, config->args, "-set_note", false, false);
+    PXOPT_LOOKUP_TIME(registered, config->args, "-set_registered", false, false);
+    PXOPT_LOOKUP_BOOL(pretend, config->args, "-pretend", false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    // Selections
+    psMetadata *where = psMetadataAlloc();
+    pxAddLabelSearchArgs(config, where, "-label", "diffRun.label", "LIKE");
+    pxAddLabelSearchArgs(config, where, "-data_group", "diffRun.data_group", "LIKE");
+    PXOPT_COPY_STR(config->args, where, "-comment", "rawExp.comment", "==");
+    PXOPT_COPY_STR(config->args, where, "-filter", "rawExp.filter", "==");
+    PXOPT_COPY_TIME(config->args, where, "-dateobs_begin", "rawExp.dateobs", ">=");
+    PXOPT_COPY_TIME(config->args, where, "-dateobs_end", "rawExp.dateobs", "<=");
+
+
+    psString query = pxDataGet("diffphottool_definerun.sql");
+    if (!query) {
+        psError(psErrorCodeLast(), false, "failed to retreive SQL statement");
+        psFree(where);
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+        psString clause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, "\nAND %s", clause);
+        psFree(clause);
+    }
+    psFree(where);
+
+    if (!psDBTransaction(config->dbh)) {
+        psError(psErrorCodeLast(), false, "database error");
+        return false;
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(psErrorCodeLast(), false, "Unable to run query: %s", query);
+        psFree(where);
+        return false;
+    }
+    psFree(where);
+
+    psArray *results = p_psDBFetchResult(config->dbh); // Results of query
+    if (!results) {
+        psError(psErrorCodeLast(), false, "database error");
+        return false;
+    }
+
+    if (!psArrayLength(results)) {
+        psTrace("diffphottool", 1, "no rows found");
+        psFree(results);
+        return true;
+    }
+
+    if (pretend) {
+        if (!ippdbPrintMetadatas(stdout, results, "diffPhotRun", !simple)) {
+            psError(psErrorCodeLast(), false, "failed to print array");
+            psFree(results);
+            return false;
+        }
+        psFree(results);
+        return true;
+    }
+
+    for (int i = 0; i < results->n; i++) {
+        psMetadata *row = results->data[i]; // Output row from query
+        bool mdok;                          // Status of MD lookup
+        psS64 diff_id = psMetadataLookupS64(&mdok, row, "diff_id");
+        const char *workdir = psMetadataLookupStr(&mdok, row, "workdir");
+        const char *label = psMetadataLookupStr(&mdok, row, "data_group");
+        const char *data_group = psMetadataLookupStr(&mdok, row, "data_group");
+        const char *reduction = psMetadataLookupStr(&mdok, row, "reduction");
+        const char *note = psMetadataLookupStr(&mdok, row, "note");
+
+        diffPhotRunRow *run = diffPhotRunRowAlloc(0, diff_id, "new",
+                                                  set_workdir ? set_workdir : workdir,
+                                                  set_label ? set_label : label,
+                                                  set_data_group ? set_data_group : data_group,
+                                                  set_reduction ? set_reduction : reduction,
+                                                  registered,
+                                                  set_note ? set_note : note);
+        if (!diffPhotRunInsertObject(config->dbh, run)) {
+            psError(psErrorCodeLast(), false, "database error");
+            if (!psDBRollback(config->dbh)) {
+                psError(psErrorCodeLast(), false, "database error");
+            }
+            psFree(run);
+            psFree(results);
+            return false;
+        }
+
+        run->diff_phot_id = psDBLastInsertID(config->dbh);
+
+        if (!diffPhotRunPrintObject(stdout, run, !simple)) {
+            psError(psErrorCodeLast(), false, "failed to print object");
+            psFree(run);
+            psFree(results);
+            return false;
+        }
+        psFree(run);
+    }
+    psFree(results);
+
+    if (!psDBCommit(config->dbh)) {
+        psError(psErrorCodeLast(), 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, "-diff_phot_id", "diff_phot_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-label", "label", "LIKE");
+    PXOPT_COPY_STR(config->args, where, "-data_group", "data_group", "LIKE");
+    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 diffPhotRun");
+
+    // pxUpdateRun gets parameters from config->args and updates
+    bool result = pxUpdateRun(config, where, &query, "diffPhotRun", "diff_phot_id", "diffPhotSkyfile", true);
+
+    psFree(query);
+    psFree(where);
+
+    return result;
+}
+
+
+static bool inputMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+
+    PXOPT_COPY_S64(config->args, where,  "-diff_phot_id", "diff_phot_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-skycell_id", "skycell_id", "==");
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psString query = pxDataGet("diffphottool_input.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+        psString clause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, "\nWHERE %s", clause);
+        psFree(clause);
+    }
+    psFree(where);
+
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, "\n%s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(psErrorCodeLast(), false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(psErrorCodeLast(), false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("diffphottool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    if (!ippdbPrintMetadatas(stdout, output, "diffSkyfile", !simple)) {
+        psError(psErrorCodeLast(), false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+    psFree(output);
+
+    return true;
+}
+
+
+static bool pendingMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where,  "-diff_phot_id", "diff_phot_id", "==");
+    pxAddLabelSearchArgs(config, where, "-label", "diffPhotRun.label", "==");
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psString query = pxDataGet("diffphottool_pending.sql");
+    if (!query) {
+        psError(psErrorCodeLast(), false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+        psString clause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, "\nAND %s", clause);
+        psFree(clause);
+    }
+    psFree(where);
+
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, "\n%s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(psErrorCodeLast(), false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(psErrorCodeLast(), false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("diffphottool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    if (!ippdbPrintMetadatas(stdout, output, "diffPhotRun", !simple)) {
+        psError(psErrorCodeLast(), false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+    psFree(output);
+
+    return true;
+}
+
+
+static bool doneMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_S64(diff_phot_id, config->args, "-diff_phot_id", true, false); // required
+    PXOPT_LOOKUP_STR(skycell_id, config->args, "-skycell_id", true, false);
+    PXOPT_LOOKUP_S16(fault, config->args, "-fault", false, false);
+    PXOPT_LOOKUP_S16(quality, config->args, "-quality", false, false);
+    PXOPT_LOOKUP_STR(path_base, config->args, "-path_base", true, false);
+    PXOPT_LOOKUP_STR(hostname, config->args, "-hostname", true, false);
+    PXOPT_LOOKUP_F32(dtime_script, config->args, "-dtime_script", 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_ppstats, config->args, "-ver_ppstats", false, false);
+    PXOPT_LOOKUP_STR(ver_psphot, config->args, "-ver_psphot", false, false);
+    PXOPT_LOOKUP_S64(magicked, config->args, "-magicked", false, false);
+
+    psString version = pxMergeCodeVersions(ver_pslib, ver_psmodules);
+    version = pxMergeCodeVersions(version, ver_ppstats);
+    version = pxMergeCodeVersions(version, ver_psphot);
+
+    if (!diffPhotSkyfileInsert(config->dbh, diff_phot_id, skycell_id, path_base, dtime_script, hostname,
+                               fault, quality, version, magicked)) {
+        psError(psErrorCodeLast(), false, "database error");
+        return false;
+    }
+
+    return true;
+}
+
+static bool advanceMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where,  "-diff_phot_id", "diff_phot_id", "==");
+    pxAddLabelSearchArgs (config, where, "-label", "diffPhotRun.label", "==");
+
+    psString query = pxDataGet("diffphottool_advance.sql");
+    if (!query) {
+        psError(psErrorCodeLast(), false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    psString whereClause = psStringCopy("");
+    if (psListLength(where->list)) {
+        psString clause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&whereClause, "\nAND %s", clause);
+        psFree(clause);
+    }
+    psFree(where);
+
+    if (!psDBTransaction(config->dbh)) {
+        psError(psErrorCodeLast(), false, "database error");
+        return false;
+    }
+
+    if (!p_psDBRunQueryF(config->dbh, query, whereClause)) {
+        psError(psErrorCodeLast(), false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+    psFree(whereClause);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(psErrorCodeLast(), false, "database error");
+        return false;
+    }
+
+    for (int i = 0; i < output->n; i++) {
+        psMetadata *row = output->data[i]; // Row of interest
+        psS64 diff_phot_id = psMetadataLookupS64(NULL, row, "diff_phot_id");
+
+        const char *query = "UPDATE diffPhotRun SET state = 'full' WHERE diff_phot_id = %" PRId64;
+        if (!p_psDBRunQueryF(config->dbh, query, diff_phot_id)) {
+            psError(psErrorCodeLast(), false,
+                    "failed to change state for diff_phot_id %" PRId64, diff_phot_id);
+            if (!psDBRollback(config->dbh)) {
+                psError(psErrorCodeLast(), false, "database error");
+            }
+            psFree(output);
+            return false;
+        }
+    }
+    psFree(output);
+
+    if (!psDBCommit(config->dbh)) {
+        psError(psErrorCodeLast(), false, "database error");
+        return false;
+    }
+
+    return true;
+}
+
+
+static bool revertMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where,  "-diff_phot_id", "diff_phot_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-skycell_id", "skycell_id", "==");
+    PXOPT_COPY_S16(config->args, where, "-fault", "fault", "==");
+    pxAddLabelSearchArgs (config, where, "-label", "label", "LIKE");
+
+    PXOPT_LOOKUP_BOOL(all, config->args, "-all", false);
+    if (!psListLength(where->list) && !all) {
+        psError(PXTOOLS_ERR_CONFIG, true, "search parameters or -all are required");
+        psFree(where);
+        return false;
+    }
+
+    psString query = pxDataGet("diffphottool_revert.sql");
+    if (!query) {
+        psError(psErrorCodeLast(), false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+        psString clause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, "\nAND %s", clause);
+        psFree(clause);
+    }
+    psFree(where);
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(psErrorCodeLast(), false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    return true;
+}
+
+static bool dataMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+
+    PXOPT_COPY_S64(config->args, where,  "-diff_phot_id", "diff_phot_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-skycell_id", "skycell_id", "==");
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psString query = pxDataGet("diffphottool_data.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+        psString clause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, "\nWHERE %s", clause);
+        psFree(clause);
+    }
+    psFree(where);
+
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, "\n%s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(psErrorCodeLast(), false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(psErrorCodeLast(), false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("diffphottool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    if (!ippdbPrintMetadatas(stdout, output, "diffPhotSkyfile", !simple)) {
+        psError(psErrorCodeLast(), false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+    psFree(output);
+
+    return true;
+}
+
Index: /branches/pap/ippTools/src/diffphottool.h
===================================================================
--- /branches/pap/ippTools/src/diffphottool.h	(revision 28484)
+++ /branches/pap/ippTools/src/diffphottool.h	(revision 28484)
@@ -0,0 +1,39 @@
+/*
+ * diffphottool.h
+ *
+ * Copyright (C) 2007-2010  Joshua Hoblitt, Paul Price
+ *
+ * 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 DIFFPHOTTOOL_H
+#define DIFFPHOTTOOL_H 1
+
+#include "pxtools.h"
+
+typedef enum {
+    DIFFPHOTTOOL_MODE_NONE           = 0x0,
+    DIFFPHOTTOOL_MODE_DEFINERUN,
+    DIFFPHOTTOOL_MODE_UPDATERUN,
+    DIFFPHOTTOOL_MODE_INPUT,
+    DIFFPHOTTOOL_MODE_PENDING,
+    DIFFPHOTTOOL_MODE_DONE,
+    DIFFPHOTTOOL_MODE_ADVANCE,
+    DIFFPHOTTOOL_MODE_REVERT,
+    DIFFPHOTTOOL_MODE_DATA,
+} diffphottoolMode;
+
+pxConfig *diffphottoolConfig(pxConfig *config, int argc, char **argv);
+
+#endif // DIFFPHOTTOOL_H
Index: /branches/pap/ippTools/src/diffphottoolConfig.c
===================================================================
--- /branches/pap/ippTools/src/diffphottoolConfig.c	(revision 28484)
+++ /branches/pap/ippTools/src/diffphottoolConfig.c	(revision 28484)
@@ -0,0 +1,161 @@
+/*
+ * diffphottoolConfig.c
+ *
+ * Copyright (C) 2007-2010  Joshua Hoblitt, Paul Price
+ *
+ * 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 <psmodules.h>
+
+#include "pxtools.h"
+#include "diffphottool.h"
+
+pxConfig *diffphottoolConfig(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;
+    }
+
+    psTime *now = psTimeGetNow(PS_TIME_TAI);
+
+    // -definerun
+    psMetadata *definerunArgs = psMetadataAlloc();
+    psMetadataAddStr(definerunArgs, PS_LIST_TAIL, "-set_workdir", 0, "define workdir (required)", NULL);
+    psMetadataAddStr(definerunArgs, PS_LIST_TAIL, "-set_label", 0, "define label", NULL);
+    psMetadataAddStr(definerunArgs, PS_LIST_TAIL, "-set_reduction", 0, "define reduction class", NULL);
+    psMetadataAddTime(definerunArgs, PS_LIST_TAIL, "-set_registered", 0, "time detrend run was registered", now);
+    psMetadataAddStr(definerunArgs, PS_LIST_TAIL, "-set_data_group", 0, "define data group", NULL);
+    psMetadataAddStr(definerunArgs, PS_LIST_TAIL, "-set_note", 0, "define note", NULL);
+    psMetadataAddStr(definerunArgs, PS_LIST_TAIL, "-label", PS_META_DUPLICATE_OK, "search by label (LIKE comparison, multiple OK)", NULL);
+    psMetadataAddStr(definerunArgs, PS_LIST_TAIL, "-data_group", PS_META_DUPLICATE_OK, "search by data_group", NULL);
+    psMetadataAddStr(definerunArgs, PS_LIST_TAIL, "-comment", 0, "search for comment (LIKE)", NULL);
+    psMetadataAddStr(definerunArgs, PS_LIST_TAIL, "-filter", 0, "search for filter", NULL);
+    psMetadataAddTime(definerunArgs, PS_LIST_TAIL, "-dateobs_begin", 0, "search for exposures by time (>=)", NULL);
+    psMetadataAddTime(definerunArgs, PS_LIST_TAIL, "-dateobs_end", 0, "search for exposures by time (<)", NULL);
+    psMetadataAddBool(definerunArgs, PS_LIST_TAIL, "-pretend",  0, "do not actually modify the database", false);
+    psMetadataAddBool(definerunArgs, PS_LIST_TAIL, "-simple", 0, "use the simple output format", false);
+
+    // -updaterun
+    psMetadata *updaterunArgs = psMetadataAlloc();
+    psMetadataAddS64(updaterunArgs, PS_LIST_TAIL, "-diff_phot_id", 0, "search by diffphot ID", 0);
+    psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-state", 0, "set state", NULL);
+    psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-label", 0, "search by label (LIKE comparison)", 0);
+    psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-data_group", 0, "search by data_group (LIKE comparison)", 0);
+    psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-set_label", 0, "define new value for label", 0);
+    psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-set_state", 0, "define new state", NULL);
+    psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-set_data_group", 0, "define new data_group", NULL);
+    psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-set_note", 0, "define new note", NULL);
+
+    // -input
+    psMetadata *inputArgs = psMetadataAlloc();
+    psMetadataAddS64(inputArgs, PS_LIST_TAIL, "-diff_phot_id", 0, "search by diffphot ID", 0);
+    psMetadataAddStr(inputArgs, PS_LIST_TAIL, "-skycell_id", 0, "search by skycell ID", NULL);
+    psMetadataAddU64(inputArgs, PS_LIST_TAIL, "-limit", 0, "limit result set to N items", 0);
+    psMetadataAddBool(inputArgs, PS_LIST_TAIL, "-simple", 0, "use the simple output format", false);
+
+    // -pending
+    psMetadata *pendingArgs = psMetadataAlloc();
+    psMetadataAddS64(pendingArgs, PS_LIST_TAIL, "-diff_phot_id", 0, "search by diffphot ID", 0);
+    psMetadataAddStr(pendingArgs, PS_LIST_TAIL, "-label", PS_META_DUPLICATE_OK, "search by label", 0);
+    psMetadataAddU64(pendingArgs, PS_LIST_TAIL, "-limit", 0, "limit result set to N items", 0);
+    psMetadataAddBool(pendingArgs, PS_LIST_TAIL, "-simple", 0, "use the simple output format", false);
+
+    // -done
+    psMetadata *doneArgs = psMetadataAlloc();
+    psMetadataAddS64(doneArgs, PS_LIST_TAIL, "-diff_phot_id", 0, "define diffphot ID (required)", 0);
+    psMetadataAddStr(doneArgs, PS_LIST_TAIL, "-skycell_id", 0, "define skycell of file (required)", NULL);
+    psMetadataAddS16(doneArgs, PS_LIST_TAIL, "-fault", 0, "set fault code", 0);
+    psMetadataAddS16(doneArgs, PS_LIST_TAIL, "-quality", 0, "set quality", 0);
+    psMetadataAddStr(doneArgs, PS_LIST_TAIL, "-path_base", 0, "define base output location (required)", NULL);
+    psMetadataAddStr(doneArgs, PS_LIST_TAIL, "-hostname", 0, "set hostname (required)", NULL);
+    psMetadataAddF32(doneArgs, PS_LIST_TAIL, "-dtime_script", 0, "define elapsed time in script (seconds)", NAN);
+    psMetadataAddStr(doneArgs, PS_LIST_TAIL, "-ver_pslib", 0, "define psLib version", NULL);
+    psMetadataAddStr(doneArgs, PS_LIST_TAIL, "-ver_psmodules", 0, "define psModules version", NULL);
+    psMetadataAddStr(doneArgs, PS_LIST_TAIL, "-ver_ppstats", 0, "define ppStats version", NULL);
+    psMetadataAddStr(doneArgs, PS_LIST_TAIL, "-ver_psphot", 0, "define psphot version", NULL);
+    psMetadataAddS64(doneArgs, PS_LIST_TAIL, "-magicked", 0, "define magicked state", 0);
+
+    // -advance
+    psMetadata *advanceArgs = psMetadataAlloc();
+    psMetadataAddS64(advanceArgs, PS_LIST_TAIL, "-diff_phot_id", 0, "select by diffphot ID", 0);
+    psMetadataAddStr(advanceArgs, PS_LIST_TAIL, "-label", PS_META_DUPLICATE_OK, "select by label", NULL);
+    psMetadataAddS32(advanceArgs, PS_LIST_TAIL, "-limit", 0, "limit number of results", 0);
+
+    // -revert
+    psMetadata *revertArgs = psMetadataAlloc();
+    psMetadataAddS64(revertArgs, PS_LIST_TAIL, "-diff_phot_id", 0, "search by diffphot ID", 0);
+    psMetadataAddStr(revertArgs, PS_LIST_TAIL, "-skycell_id", 0, "search by skycell_id", NULL);
+    psMetadataAddStr(revertArgs, PS_LIST_TAIL, "-label", PS_META_DUPLICATE_OK, "search by label", NULL);
+    psMetadataAddS16(revertArgs, PS_LIST_TAIL, "-fault", 0, "search by fault code", 0);
+    psMetadataAddBool(revertArgs, PS_LIST_TAIL, "-all", 0, "allow no search terms", 0);
+
+    // -data
+    psMetadata *dataArgs = psMetadataAlloc();
+    psMetadataAddS64(dataArgs, PS_LIST_TAIL, "-diff_phot_id", 0, "search by diffphot ID", 0);
+    psMetadataAddStr(dataArgs, PS_LIST_TAIL, "-skycell_id", 0, "search by skycell ID", NULL);
+    psMetadataAddU64(dataArgs, PS_LIST_TAIL, "-limit", 0, "limit result set to N items", 0);
+    psMetadataAddBool(dataArgs, PS_LIST_TAIL, "-simple", 0, "use the simple output format", false);
+
+    psFree(now);
+
+    psMetadata *argSets = psMetadataAlloc();
+    psMetadata *modes = psMetadataAlloc();
+
+    PXOPT_ADD_MODE("-definerun", "", DIFFPHOTTOOL_MODE_DEFINERUN, definerunArgs);
+    PXOPT_ADD_MODE("-updaterun", "", DIFFPHOTTOOL_MODE_UPDATERUN, updaterunArgs);
+    PXOPT_ADD_MODE("-input",     "", DIFFPHOTTOOL_MODE_INPUT,     inputArgs);
+    PXOPT_ADD_MODE("-pending",   "", DIFFPHOTTOOL_MODE_PENDING,   pendingArgs);
+    PXOPT_ADD_MODE("-done",      "", DIFFPHOTTOOL_MODE_DONE,      doneArgs);
+    PXOPT_ADD_MODE("-advance",   "", DIFFPHOTTOOL_MODE_ADVANCE,   advanceArgs);
+    PXOPT_ADD_MODE("-revert",    "", DIFFPHOTTOOL_MODE_REVERT,    revertArgs);
+    PXOPT_ADD_MODE("-data",      "", DIFFPHOTTOOL_MODE_DATA,      dataArgs);
+
+    if (!pxGetOptions(stderr, argc, argv, config, modes, argSets)) {
+        psError(PS_ERR_UNKNOWN, true, "option parsing failed");
+        psFree(argSets);
+        psFree(modes);
+        psFree(config);
+        return NULL;
+    }
+
+    psFree(argSets);
+    psFree(modes);
+
+    // define Database handle, if used
+    // do this last so we don't setup a connection before CLI options are
+    // validated
+    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/pap/ippTools/src/difftool.c
===================================================================
--- /branches/pap/ippTools/src/difftool.c	(revision 28483)
+++ /branches/pap/ippTools/src/difftool.c	(revision 28484)
@@ -44,4 +44,6 @@
 static bool definewarpwarpMode(pxConfig *config);
 static bool definestackstackMode(pxConfig *config);
+static bool tosummaryMode(pxConfig *config);
+static bool addsummaryMode(pxConfig *config);
 static bool pendingcleanuprunMode(pxConfig *config);
 static bool pendingcleanupskyfileMode(pxConfig *config);
@@ -93,4 +95,6 @@
         MODECASE(DIFFTOOL_MODE_DEFINEWARPWARP,        definewarpwarpMode);
         MODECASE(DIFFTOOL_MODE_DEFINESTACKSTACK,      definestackstackMode);
+	MODECASE(DIFFTOOL_MODE_TOSUMMARY,             tosummaryMode);
+	MODECASE(DIFFTOOL_MODE_ADDSUMMARY,            addsummaryMode);
         MODECASE(DIFFTOOL_MODE_PENDINGCLEANUPRUN,     pendingcleanuprunMode);
         MODECASE(DIFFTOOL_MODE_PENDINGCLEANUPSKYFILE, pendingcleanupskyfileMode);
@@ -2250,4 +2254,135 @@
 }
 
+static bool tosummaryMode(pxConfig *config) {
+  PS_ASSERT_PTR_NON_NULL(config, NULL);
+  
+  psMetadata *where = psMetadataAlloc();
+  PXOPT_COPY_S64(config->args, where, "-warp_id",    "diffSkyfile.warp_id", "==");
+  PXOPT_COPY_STR(config->args, where, "-tess_id",    "diffSkyfile.tess_id", "==");
+  PXOPT_COPY_STR(config->args, where, "-state",      "diffRun.state", "==");
+  PXOPT_COPY_TIME(config->args, where, "-dateobs_begin", "rawExp.dateobs",  ">=");
+  PXOPT_COPY_TIME(config->args, where, "-dateobs_end",   "rawExp.dateobs",  "<=");
+  PXOPT_COPY_STR(config->args, where, "-filter",    "rawExp.filter", "LIKE");
+  PXOPT_COPY_S64(config->args, where, "-magicked", "diffSkyfile.magicked", "==");
+  pxAddLabelSearchArgs (config, where, "-label",   "diffRun.label", "LIKE");
+  pxAddLabelSearchArgs (config, where, "-data_group",   "diffRun.data_group", "LIKE");
+
+  PXOPT_LOOKUP_BOOL(all, config->args, "-all", false);
+
+  PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+  PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+  
+  // find all rawImfiles matching the default query
+  psString query = pxDataGet("difftool_tosummary.sql");
+  if (!query) {
+    psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+    return false;
+  }
+
+  // generate where strings for arguments that require extra processing
+  // beyond PXOPT_COPY*
+  psString where2 = NULL;
+  if (!pxmagicAddWhere(config, &where2, "diffSkyfile")) {
+    psError(psErrorCodeLast(), false, "pxMagicAddWhere failed");
+    return false;
+  }
+  
+  if (psListLength(where->list)) {
+    psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+    psStringAppend(&query, " AND %s", whereClause);
+    psFree(whereClause);
+  } else if (!all && !where2) {
+    psError(PXTOOLS_ERR_CONFIG, true, "search parameters or -all are required");
+    return false;
+  }
+  
+  if (where2) {
+    if (psListLength(where->list)) {
+      psStringAppend(&query, " %s", where2);
+    } else {
+      psStringAppend(&query, " AND 1 %s", where2);
+    }
+  }
+  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("difftool", PS_LOG_INFO, "no rows found");
+    psFree(output);
+    return true;
+  }
+  
+  if (psArrayLength(output)) {
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "diffRun", !simple)) {
+      psError(PS_ERR_UNKNOWN, false, "failed to print array");
+      psFree(output);
+      return false;
+    }
+  }
+  
+  psFree(output);
+  return(true);
+}
+static bool addsummaryMode(pxConfig *config) {
+  PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+  PXOPT_LOOKUP_S64(diff_id, config->args, "-diff_id", true, false);
+  PXOPT_LOOKUP_STR(projection_cell, config->args, "-projection_cell", true, false);
+  PXOPT_LOOKUP_STR(path_base, config->args, "-path_base", true, false);
+
+  psString query = pxDataGet("difftool_addsummary.sql");
+  if (!query) {
+    psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+    return(false);
+  }
+  if (!p_psDBRunQueryF(config->dbh, query, diff_id, projection_cell, path_base)) {
+    psError(PS_ERR_UNKNOWN, false, "database error");
+    psFree(query);
+    return(false);
+  }
+  psS64 numUpdated = psDBAffectedRows(config->dbh);
+  
+  if (numUpdated != 1) {
+    psError(PS_ERR_UNKNOWN, false, "should have affected 1 row");
+    psFree(query);
+    return false;
+  }
+  
+  psFree(query);
+
+  // Print anything here?
+  
+  return(true);
+}
+
+
 static bool pendingcleanuprunMode(pxConfig *config)
 {
Index: /branches/pap/ippTools/src/difftool.h
===================================================================
--- /branches/pap/ippTools/src/difftool.h	(revision 28483)
+++ /branches/pap/ippTools/src/difftool.h	(revision 28484)
@@ -32,4 +32,6 @@
     DIFFTOOL_MODE_ADDDIFFSKYFILE,
     DIFFTOOL_MODE_ADVANCE,
+    DIFFTOOL_MODE_TOSUMMARY,
+    DIFFTOOL_MODE_ADDSUMMARY,
     DIFFTOOL_MODE_DIFFSKYFILE,
     DIFFTOOL_MODE_REVERTDIFFSKYFILE,
Index: /branches/pap/ippTools/src/difftoolConfig.c
===================================================================
--- /branches/pap/ippTools/src/difftoolConfig.c	(revision 28483)
+++ /branches/pap/ippTools/src/difftoolConfig.c	(revision 28484)
@@ -323,4 +323,33 @@
     psMetadataAddU64(pendingcleanuprunArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
 
+    // -tosummary
+    psMetadata *tosummaryArgs = psMetadataAlloc();
+    psMetadataAddS64(tosummaryArgs, PS_LIST_TAIL,  "-diff_id", 0,           "search by diff ID", 0);
+    psMetadataAddStr(tosummaryArgs, PS_LIST_TAIL,  "-tess_id",  0,          "search by tessellation ID", NULL);
+    psMetadataAddStr(tosummaryArgs, PS_LIST_TAIL, "-state",  0,            "search by state", NULL);
+    psMetadataAddS64(tosummaryArgs, PS_LIST_TAIL,  "-exp_id",  0,           "search by exposure ID", 0);
+    psMetadataAddStr(tosummaryArgs , PS_LIST_TAIL, "-exp_name",  0,        "search by exposure name", NULL);
+    psMetadataAddS64(tosummaryArgs, PS_LIST_TAIL, "-warp_id", 0,         "search by warp ID", 0);
+    
+    psMetadataAddTime(tosummaryArgs, PS_LIST_TAIL, "-dateobs_begin", 0,    "search for exposures by time (>=)", NULL);
+    psMetadataAddTime(tosummaryArgs, PS_LIST_TAIL, "-dateobs_end", 0,      "search for exposures by time (<=)", NULL);
+    psMetadataAddStr(tosummaryArgs, PS_LIST_TAIL,  "-filter", 0,           "search for filter", NULL);
+    psMetadataAddS64(tosummaryArgs, PS_LIST_TAIL,  "-magicked", 0,         "search by magic id", 0);
+    psMetadataAddStr(tosummaryArgs,  PS_LIST_TAIL, "-label",  PS_META_DUPLICATE_OK, "search by diffRun label (LIKE comparison)", NULL);
+    psMetadataAddStr(tosummaryArgs,  PS_LIST_TAIL, "-data_group",  PS_META_DUPLICATE_OK, "search by diffRun data_group (LIKE comparison)", NULL);
+    psMetadataAddStr(tosummaryArgs,  PS_LIST_TAIL, "-dist_group",  PS_META_DUPLICATE_OK, "search by diffRun dist_group (LIKE comparison)", NULL);
+    psMetadataAddBool(tosummaryArgs, PS_LIST_TAIL,  "-destreaked", 0, "search for runs that have been destreaked", false);
+    psMetadataAddBool(tosummaryArgs, PS_LIST_TAIL,  "-not_destreaked", 0, "search for runs that have not been destreaked", false);
+    
+    psMetadataAddBool(tosummaryArgs, PS_LIST_TAIL, "-all",  0,             "search without arguments", false);
+    psMetadataAddU64(tosummaryArgs, PS_LIST_TAIL,  "-limit",  0,            "limit result set to N items", 0);
+    psMetadataAddBool(tosummaryArgs, PS_LIST_TAIL, "-simple",  0,          "use the simple output format", false);
+    
+    // -addsummary
+    psMetadata *addsummaryArgs = psMetadataAlloc();
+    psMetadataAddS64(addsummaryArgs, PS_LIST_TAIL,  "-diff_id", 0,           "search by diff ID", 0);
+    psMetadataAddStr(addsummaryArgs, PS_LIST_TAIL, "-projection_cell", 0, "set projection cell", NULL);
+    psMetadataAddStr(addsummaryArgs, PS_LIST_TAIL, "-path_base", 0, "set summary path base", NULL);
+
     // -pendingcleanupskyfile
     psMetadata *pendingcleanupskyfileArgs = psMetadataAlloc();
@@ -380,5 +409,4 @@
     psMetadata *importrunArgs = psMetadataAlloc();
     psMetadataAddStr(importrunArgs, PS_LIST_TAIL, "-infile",  0,          "import from this file (required)", NULL);
-
 
 
@@ -415,4 +443,6 @@
     PXOPT_ADD_MODE("-toscrubbedskyfile", "set skyfile as scrubbed", DIFFTOOL_MODE_TOSCRUBBEDSKYFILE, toscrubbedskyfileArgs);
     PXOPT_ADD_MODE("-tofullskyfile", "set skyfile as full", DIFFTOOL_MODE_TOFULLSKYFILE, tofullskyfileArgs);
+    PXOPT_ADD_MODE("-tosummary",            "show runs that can be summarized", DIFFTOOL_MODE_TOSUMMARY, tosummaryArgs);
+    PXOPT_ADD_MODE("-addsummary",           "add entry to the summary table", DIFFTOOL_MODE_ADDSUMMARY, addsummaryArgs);
 
     if (!pxGetOptions(stderr, argc, argv, config, modes, argSets)) {
Index: /branches/pap/ippTools/src/disttool.c
===================================================================
--- /branches/pap/ippTools/src/disttool.c	(revision 28483)
+++ /branches/pap/ippTools/src/disttool.c	(revision 28484)
@@ -148,5 +148,5 @@
     PXOPT_LOOKUP_BOOL(pretend, config->args, "-pretend", false);
 
-    if (use_alternate) { 
+    if (use_alternate) {
         if (strcmp(stage, "raw")) {
             psError(PXTOOLS_ERR_SYS, true, "alternate inputs only supported for raw stage");
@@ -299,17 +299,17 @@
       query = pxDataGet("disttool_definebyquery_SSdiff.sql");
       if (!query) {
-	psError(PXTOOLS_ERR_SYS, false, "failed to retrieve SQL statement");
-	psFree(where);
-	return(false);
+        psError(PXTOOLS_ERR_SYS, false, "failed to retrieve SQL statement");
+        psFree(where);
+        return(false);
       }
 
       if (label) {
-	psStringAppend(&query, " AND (diffRun.label = '%s') ", label);
+        psStringAppend(&query, " AND (diffRun.label = '%s') ", label);
       }
       if (dist_group) {
-	psStringAppend(&query, " AND (diffRun.dist_group = '%s') ", dist_group);
+        psStringAppend(&query, " AND (diffRun.dist_group = '%s') ", dist_group);
       }
 
-      no_magic = true;    
+      no_magic = true;
     } else {
         psError(PS_ERR_UNKNOWN, true, "unknown value for stage: %s", stage);
@@ -470,5 +470,14 @@
     PXOPT_COPY_STR(config->args, where, "-data_group", "distRun.data_group", "LIKE");
     PXOPT_COPY_STR(config->args, where, "-dist_group", "distTarget.dist_group", "==");
-    PXOPT_COPY_STR(config->args, where, "-label", "label", "==");
+    PXOPT_COPY_TIME(config->args, where, "-time_stamp_begin", "distRun.time_stamp", ">=");
+    PXOPT_COPY_TIME(config->args, where, "-time_stamp_end", "distRun.time_stamp", "<=");
+
+    PXOPT_LOOKUP_BOOL(clean, config->args, "-clean", false);
+    PXOPT_LOOKUP_BOOL(full, config->args, "-full", false);
+    if (clean && full) {
+        psFree(where);
+        psError(PXTOOLS_ERR_CONFIG, true, "-clean and -full are contradictory parameters");
+        return false;
+    }
 
     if (!psListLength(where->list)) {
@@ -491,4 +500,5 @@
     }
 
+    psString extraWhere = NULL;
     psString query = psStringCopy("UPDATE distRun join distTarget using(target_id, stage) SET distRun.time_stamp = UTC_TIMESTAMP()");
 
@@ -498,4 +508,8 @@
     if (state) {
         psStringAppend(&query, " , distRun.state = '%s'", state);
+        if (!strcmp(state, "goto_cleaned")) {
+            // don't queue for clean up if run has already already cleaned
+            psStringAppend(&extraWhere, " AND (distRun.state != 'cleaned' AND distRun.state != 'goto_cleaned')");
+        }
     }
 
@@ -521,4 +535,13 @@
     psFree(whereClause);
     psFree(where);
+
+    if (extraWhere) {
+        psStringAppend(&query, "%s", extraWhere);
+    }
+    if (clean) {
+        psStringAppend(&query, " AND (distRun.clean)");
+    } else if (full) {
+        psStringAppend(&query, " AND (!distRun.clean)");
+    }
 
     if (!p_psDBRunQuery(config->dbh, query)) {
@@ -1680,5 +1703,5 @@
         return false;
     }
-        
+
 
     PXOPT_LOOKUP_STR(state, config->args, "-set_state", false, false);
Index: /branches/pap/ippTools/src/disttoolConfig.c
===================================================================
--- /branches/pap/ippTools/src/disttoolConfig.c	(revision 28483)
+++ /branches/pap/ippTools/src/disttoolConfig.c	(revision 28484)
@@ -79,5 +79,10 @@
     psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-data_group",     0, "limit updates to data_group", NULL);
     psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-dist_group",     0, "limit updates to data_group", NULL);
+    psMetadataAddTime(updaterunArgs, PS_LIST_TAIL, "-time_stamp_begin", 0, "limit updates by time_stamp (>=)", NULL); 
+    psMetadataAddTime(updaterunArgs, PS_LIST_TAIL, "-time_stamp_end", 0, "limit updates by time_stamp (<=)", NULL); 
+    psMetadataAddBool(updaterunArgs, PS_LIST_TAIL,"-clean",     0, "limit updates to clean distRuns", false);
+    psMetadataAddBool(updaterunArgs, PS_LIST_TAIL,"-full",      0, "limit updates to not clean distRuns", false);
     psMetadataAddS16(updaterunArgs, PS_LIST_TAIL, "-fault",      0, "define fault code", 0);
+
     // -revertrun
     psMetadata *revertrunArgs = psMetadataAlloc();
Index: /branches/pap/ippTools/src/dqstatstool.c
===================================================================
--- /branches/pap/ippTools/src/dqstatstool.c	(revision 28483)
+++ /branches/pap/ippTools/src/dqstatstool.c	(revision 28484)
@@ -85,5 +85,5 @@
 
   if (label) {
-    PXOPT_COPY_STR(config->args, where, "-label", "camRun.label", "==");
+    pxAddLabelSearchArgs (config, where, "-label", "camRun.label", "LIKE"); // define using cam label
   }
   // use psDBGenerateWhereConditionSQL because the SQL ends in a WHERE
@@ -654,5 +654,15 @@
 	psError(PS_EXIT_CONFIG_ERROR, false, "Unable to find max value for %s",colname);
 	return(false);
-      }      
+      }
+      psString filter = psMetadataLookupStr(&status,rule,"FILTER");
+      if (!status) {
+	status = true;
+      }
+      if (filter) {
+	psString imfilter = psMetadataLookupStr(&status,tableRow,"FILTER");
+	if (strcmp(filter,imfilter) != 0) {
+	  continue;
+	}
+      }
       // Not happy with this being set to a F32. Can this ever be something else?
       psF32 value      = psMetadataLookupF32(&status,tableRow,colname);
Index: /branches/pap/ippTools/src/flatcorr.c
===================================================================
--- /branches/pap/ippTools/src/flatcorr.c	(revision 28483)
+++ /branches/pap/ippTools/src/flatcorr.c	(revision 28484)
@@ -671,5 +671,8 @@
                 row->dvodb,
                 NULL,       // note is not propagated
-                0)) {
+                0,
+		0,  //The minidvodb stuff is off
+		NULL,
+		NULL)) {
             if (!psDBRollback(config->dbh)) {
                 psError(PS_ERR_UNKNOWN, false, "database error");
Index: /branches/pap/ippTools/src/magictool.c
===================================================================
--- /branches/pap/ippTools/src/magictool.c	(revision 28483)
+++ /branches/pap/ippTools/src/magictool.c	(revision 28484)
@@ -1367,4 +1367,5 @@
             (strncmp(state, "new", 4) == 0)
             || (strncmp(state, "full", 5) == 0)
+            || (strncmp(state, "drop", 5) == 0)
             || (strncmp(state, "reg", 4) == 0)
         )
Index: /branches/pap/ippTools/src/pstamptool.c
===================================================================
--- /branches/pap/ippTools/src/pstamptool.c	(revision 28483)
+++ /branches/pap/ippTools/src/pstamptool.c	(revision 28484)
@@ -271,4 +271,5 @@
         uri,
         NULL,   // outdir
+        NULL,   // timestamp
         0       // fault
         )) {
@@ -547,4 +548,9 @@
     PXOPT_LOOKUP_BOOL(clearfault,config->args, "-clearfault",    false);
 
+    if (!state && !label && !outProduct && !fault && !uri && !outdir && !name && !reqType && !clearfault) {
+        psError(PS_ERR_UNKNOWN, true, "at least one set option is required");
+        return false;
+    }
+
     psMetadata *where = psMetadataAlloc();
     PXOPT_COPY_S64(config->args, where, "-req_id",     "req_id", "==");
@@ -552,4 +558,6 @@
     PXOPT_COPY_S32(config->args, where, "-fault",      "fault", "==");
     PXOPT_COPY_STR(config->args, where, "-state",      "state", "==");
+    PXOPT_COPY_TIME(config->args, where, "-timestamp_begin", "timestamp", ">=");
+    PXOPT_COPY_TIME(config->args, where, "-timestamp_end", "timestamp", "<=");
     pxAddLabelSearchArgs(config, where, "-label",      "pstampRequest.label", "LIKE");
     if (!psListLength(where->list)) {
@@ -559,11 +567,9 @@
     }
 
-    psString query = psStringCopy("UPDATE pstampRequest SET");
+    psString query = psStringCopy("UPDATE pstampRequest SET timestamp = UTC_TIMESTAMP()");
 
     psString stateCheck = NULL;
-    char c = ' ';
     if (state) {
-        psStringAppend(&query, "%c state = '%s'", c, state);
-        c = ',';
+        psStringAppend(&query, ", state = '%s'", state);
         if (!strcmp(state, "goto_cleaned")) {
             psStringAppend(&stateCheck, " AND state != 'cleaned'");
@@ -571,14 +577,11 @@
     }
     if (label) {
-        psStringAppend(&query, "%c label = '%s'", c, label);
-        c = ',';
+        psStringAppend(&query, ", label = '%s'", label);
     }
     if (outProduct) {
-        psStringAppend(&query, "%c outProduct = '%s'", c, outProduct);
-        c = ',';
+        psStringAppend(&query, ", outProduct = '%s'", outProduct);
     }
     if (outdir) {
-        psStringAppend(&query, "%c outdir = '%s'", c, outdir);
-        c = ',';
+        psStringAppend(&query, ", outdir = '%s'", outdir);
     }
     if (clearfault) {
@@ -587,25 +590,16 @@
             return false;
         }
-        psStringAppend(&query, "%c fault = 0", c);
-        c = ',';
+        psStringAppend(&query, ", fault = 0");
     } else if (fault) {
-        psStringAppend(&query, "%c fault = %d", c, fault);
-        c = ',';
+        psStringAppend(&query, ", fault = %d", fault);
     }
     if (uri) {
-        psStringAppend(&query, "%c uri = '%s'", c, uri);
-        c = ',';
+        psStringAppend(&query, ", uri = '%s'", uri);
     }
     if (name) {
-        psStringAppend(&query, "%c name = '%s'", c, name);
-        c = ',';
+        psStringAppend(&query, ", name = '%s'", name);
     }
     if (reqType) {
-        psStringAppend(&query, "%c reqType = '%s'", c, reqType);
-        c = ',';
-    }
-    if (c != ',') {
-        psError(PS_ERR_UNKNOWN, true, "at least one set option is required");
-        return false;
+        psStringAppend(&query, ", reqType = '%s'", reqType);
     }
 
@@ -876,8 +870,9 @@
 
     PXOPT_LOOKUP_S64(job_id,    config->args, "-job_id", false, false);
+    PXOPT_LOOKUP_S64(req_id,    config->args, "-req_id", false, false);
     PXOPT_LOOKUP_S64(dep_id,    config->args, "-dep_id", false, false);
 
-    if (!job_id && !dep_id) {
-        psError(PS_ERR_UNKNOWN, true, "at least -job_id or -dep_id is required");
+    if (!job_id && !req_id && !dep_id) {
+        psError(PS_ERR_UNKNOWN, true, "at least one of -job_id -req_id or -dep_id is required");
         return false;
     }
@@ -894,5 +889,7 @@
 
     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, "-dep_id", "dep_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-state",  "pstampJob.state", "==");
 
     psString query = pxDataGet("pstamptool_updatejob.sql");
Index: /branches/pap/ippTools/src/pstamptoolConfig.c
===================================================================
--- /branches/pap/ippTools/src/pstamptoolConfig.c	(revision 28483)
+++ /branches/pap/ippTools/src/pstamptoolConfig.c	(revision 28484)
@@ -103,4 +103,6 @@
     psMetadataAddStr(updatereqArgs, PS_LIST_TAIL, "-state", 0,        "search by state", NULL);
     psMetadataAddStr(updatereqArgs, PS_LIST_TAIL, "-label", PS_META_DUPLICATE_OK, "search by pstampJob label (LIKE comparision)", NULL);
+    psMetadataAddTime(updatereqArgs, PS_LIST_TAIL, "-timestamp_begin", 0, "search by timestamp (>=)", NULL);
+    psMetadataAddTime(updatereqArgs, PS_LIST_TAIL, "-timestamp_end", 0, "search by timestamp (<=)", NULL);
     psMetadataAddStr(updatereqArgs, PS_LIST_TAIL, "-set_state", 0,        "new state", NULL);
     psMetadataAddStr(updatereqArgs, PS_LIST_TAIL, "-set_label", 0,        "new label", NULL);
@@ -158,6 +160,8 @@
     // -updatejob
     psMetadata *updatejobArgs = psMetadataAlloc();
-    psMetadataAddS64(updatejobArgs, PS_LIST_TAIL, "-job_id", 0,            "req_id for which to change state", 0);
-    psMetadataAddS64(updatejobArgs, PS_LIST_TAIL, "-dep_id", 0,            "dep_id for which to change state", 0);
+    psMetadataAddS64(updatejobArgs, PS_LIST_TAIL, "-req_id", 0,            "req_id of jobs to update", 0);
+    psMetadataAddS64(updatejobArgs, PS_LIST_TAIL, "-job_id", 0,            "job_id of jobs to update", 0);
+    psMetadataAddS64(updatejobArgs, PS_LIST_TAIL, "-dep_id", 0,            "dep_id of jobs to update", 0);
+    psMetadataAddStr(updatejobArgs, PS_LIST_TAIL, "-state", 0,            "current state of jobs to update", 0);
     psMetadataAddStr(updatejobArgs, PS_LIST_TAIL, "-set_state", 0,            "new state", NULL);
     psMetadataAddS16(updatejobArgs, PS_LIST_TAIL, "-set_fault", 0,            "new result", 0);
Index: /branches/pap/ippTools/src/pubtool.c
===================================================================
--- /branches/pap/ippTools/src/pubtool.c	(revision 28483)
+++ /branches/pap/ippTools/src/pubtool.c	(revision 28484)
@@ -151,37 +151,97 @@
     PS_ASSERT_PTR_NON_NULL(config, false);
 
-    psMetadata *where = psMetadataAlloc(); // WHERE conditions
+    psMetadata *diffWhere = psMetadataAlloc(); // WHERE conditions for diffs
+    psMetadata *camWhere = psMetadataAlloc(); // WHERE conditions for cams
+    psMetadata *diffphotWhere = psMetadataAlloc(); // WHERE conditions for diffphots
 
     // required
 
     // optional
-    PXOPT_COPY_S64(config->args, where, "-client_id", "client_id", "==");
-    pxAddLabelSearchArgs(config, where, "-label", "label", "=="); // define using newExp label
+    PXOPT_COPY_S64(config->args, diffWhere, "-client_id", "client_id", "==");
+    pxAddLabelSearchArgs(config, diffWhere, "-label", "diffRun.label", "==");
+    pxAddLabelSearchArgs(config, diffWhere, "-data_group", "diffRun.data_group", "LIKE");
+    PXOPT_COPY_TIME(config->args, diffWhere, "-dateobs_begin", "rawExp.dateobs", ">=");
+    PXOPT_COPY_TIME(config->args, diffWhere, "-dateobs_end", "rawExp.dateobs", "<=");
+    PXOPT_COPY_STR(config->args, diffWhere, "-filter", "rawExp.filter", "LIKE");
+    PXOPT_COPY_STR(config->args, diffWhere, "-obs_mode", "rawExp.obs_mode", "LIKE");
+
+    PXOPT_COPY_S64(config->args, camWhere, "-client_id", "client_id", "==");
+    pxAddLabelSearchArgs(config, camWhere, "-label", "camRun.label", "==");
+    pxAddLabelSearchArgs(config, camWhere, "-data_group", "camRun.data_group", "LIKE");
+    PXOPT_COPY_TIME(config->args, camWhere, "-dateobs_begin", "rawExp.dateobs", ">=");
+    PXOPT_COPY_TIME(config->args, camWhere, "-dateobs_end", "rawExp.dateobs", "<=");
+    PXOPT_COPY_STR(config->args, camWhere, "-filter", "rawExp.filter", "LIKE");
+    PXOPT_COPY_STR(config->args, camWhere, "-obs_mode", "rawExp.obs_mode", "LIKE");
+
+    PXOPT_COPY_S64(config->args, diffphotWhere, "-client_id", "client_id", "==");
+    pxAddLabelSearchArgs(config, diffphotWhere, "-label", "diffphotRun.label", "==");
+    pxAddLabelSearchArgs(config, diffphotWhere, "-data_group", "diffphotRun.data_group", "LIKE");
+    PXOPT_COPY_TIME(config->args, diffphotWhere, "-dateobs_begin", "rawExp.dateobs", ">=");
+    PXOPT_COPY_TIME(config->args, diffphotWhere, "-dateobs_end", "rawExp.dateobs", "<=");
+    PXOPT_COPY_STR(config->args, diffphotWhere, "-filter", "rawExp.filter", "LIKE");
+    PXOPT_COPY_STR(config->args, diffphotWhere, "-obs_mode", "rawExp.obs_mode", "LIKE");
+
+    PXOPT_LOOKUP_STR(set_label, config->args, "-set_label", false, false);
+    PXOPT_LOOKUP_BOOL(rerun, config->args, "-rerun", false);
+    PXOPT_LOOKUP_BOOL(pretend, config->args, "-pretend", false);
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
 
     psString query = pxDataGet("pubtool_definerun.sql"); // Query to run
     if (!query) {
         psError(PXTOOLS_ERR_SYS, false, "Failed to retreive SQL statement");
-        psFree(where);
-        return false;
+        psFree(diffWhere);
+        psFree(camWhere);
+        return false;
+    }
+
+    if (!rerun) {
+        psStringAppend(&query, "\nWHERE publishRun.client_id IS NULL");
+    }
+
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, "\n%s", limitString);
+        psFree(limitString);
     }
 
     if (!psDBTransaction(config->dbh)) {
         psError(PS_ERR_UNKNOWN, false, "Database error");
-        psFree(where);
-        return false;
-    }
-
-    psString whereClause = psStringCopy(""); // Additional constraints to add to query
-    if (psListLength(where->list)) {
-        psString clause = psDBGenerateWhereConditionSQL(where, NULL);
-        psStringAppend(&whereClause, "\n AND %s", clause);
+        psFree(diffWhere);
+        psFree(camWhere);
+        psFree(diffphotWhere);
+        return false;
+    }
+
+    psString whereDiff = psStringCopy(""); // Additional constraints to add to query
+    if (psListLength(diffWhere->list)) {
+        psString clause = psDBGenerateWhereConditionSQL(diffWhere, NULL);
+        psStringAppend(&whereDiff, "\n AND %s", clause);
         psFree(clause);
     }
-    psFree(where);
-
-    if (!p_psDBRunQueryF(config->dbh, query, whereClause, whereClause)) {
+    psFree(diffWhere);
+
+    psString whereCam = psStringCopy(""); // Additional constraints to add to query
+    if (psListLength(camWhere->list)) {
+        psString clause = psDBGenerateWhereConditionSQL(camWhere, NULL);
+        psStringAppend(&whereCam, "\n AND %s", clause);
+        psFree(clause);
+    }
+    psFree(camWhere);
+
+    psString whereDiffphot = psStringCopy(""); // Additional constraints to add to query
+    if (psListLength(diffphotWhere->list)) {
+        psString clause = psDBGenerateWhereConditionSQL(diffphotWhere, NULL);
+        psStringAppend(&whereDiffphot, "\n AND %s", clause);
+        psFree(clause);
+    }
+    psFree(diffphotWhere);
+
+    if (!p_psDBRunQueryF(config->dbh, query, whereDiff, whereCam, whereDiffphot)) {
         psError(PS_ERR_UNKNOWN, false, "Database error");
         psFree(query);
-        psFree(whereClause);
+        psFree(whereDiff);
+        psFree(whereCam);
+        psFree(whereDiffphot);
         if (!psDBRollback(config->dbh)) {
             psError(PS_ERR_UNKNOWN, false, "Database error");
@@ -190,5 +250,7 @@
     }
     psFree(query);
-    psFree(whereClause);
+    psFree(whereDiff);
+    psFree(whereCam);
+    psFree(whereDiffphot);
 
     psArray *output = p_psDBFetchResult(config->dbh); // Output of SELECT statement
@@ -206,11 +268,21 @@
     }
 
+    if (pretend) {
+        if (!ippdbPrintMetadatas(stdout, output, "publishRun", !simple)) {
+            psError(psErrorCodeLast(), false, "Failed to print array");
+            psFree(output);
+            return false;
+        }
+        psFree(output);
+        return true;
+    }
+
     for (int i = 0; i < output->n; i++) {
         psMetadata *row = output->data[i]; // Row of interest
         psS64 client = psMetadataLookupS64(NULL, row, "client_id"); // Client identifier
         psS64 stage = psMetadataLookupS64(NULL, row, "stage_id");   // Stage identifier
-        char *label = psMetadataLookupStr(NULL, row, "src_label");   // label from correct source
-
-        if (!publishRunInsert(config->dbh, 0, client, stage, label, "new")) {
+        const char *label = psMetadataLookupStr(NULL, row, "src_label");   // label from correct source
+
+        if (!publishRunInsert(config->dbh, 0, client, stage, set_label ? set_label : label, "new")) {
             psError(PS_ERR_UNKNOWN, false, "Unable to add fileset");
             psFree(output);
@@ -241,5 +313,5 @@
     PXOPT_COPY_STR(config->args, where, "-stage", "publishClient.stage", "==");
     PXOPT_COPY_STR(config->args, where, "-comment", "publishClient.comment", "LIKE");
-    PXOPT_COPY_STR(config->args, where, "-label", "publishRun.label", "==");
+    pxAddLabelSearchArgs(config, where, "-label", "publishRun.label", "==");
 
     // optional
Index: /branches/pap/ippTools/src/pubtoolConfig.c
===================================================================
--- /branches/pap/ippTools/src/pubtoolConfig.c	(revision 28483)
+++ /branches/pap/ippTools/src/pubtoolConfig.c	(revision 28484)
@@ -62,6 +62,16 @@
     // -definerun
     psMetadata *definerunArgs = psMetadataAlloc();
+    psMetadataAddStr(definerunArgs, PS_LIST_TAIL, "-set_label", 0, "set label", NULL);
     psMetadataAddS64(definerunArgs, PS_LIST_TAIL, "-client_id", 0, "search by client_id", 0);
     psMetadataAddStr(definerunArgs, PS_LIST_TAIL, "-label", PS_META_DUPLICATE_OK, "set and search by label", NULL);
+    psMetadataAddStr(definerunArgs, PS_LIST_TAIL, "-data_group", PS_META_DUPLICATE_OK, "search by data_group", NULL);
+    psMetadataAddTime(definerunArgs, PS_LIST_TAIL, "-dateobs_begin", 0, "search for exposures by time (>=)", NULL);
+    psMetadataAddTime(definerunArgs, PS_LIST_TAIL, "-dateobs_end", 0, "search for exposures by time (<=)", NULL);
+    psMetadataAddStr(definerunArgs,  PS_LIST_TAIL, "-filter", 0, "search for filter", NULL);
+    psMetadataAddStr(definerunArgs, PS_LIST_TAIL, "-obs_mode", 0, "search by observation mode", NULL);
+    psMetadataAddBool(definerunArgs, PS_LIST_TAIL, "-rerun", 0, "Re-run publish?", false);
+    psMetadataAddBool(definerunArgs, PS_LIST_TAIL, "-pretend", 0, "Pretend to define?", false);
+    psMetadataAddBool(definerunArgs, PS_LIST_TAIL, "-simple",  0, "use simple output format?", false);
+    psMetadataAddU64(definerunArgs, PS_LIST_TAIL, "-limit",  0, "limit result set", 0);
 
     // -pending
@@ -70,5 +80,5 @@
     psMetadataAddStr(pendingArgs, PS_LIST_TAIL, "-stage", 0, "search on source", NULL);
     psMetadataAddStr(pendingArgs, PS_LIST_TAIL, "-comment", 0, "search on comment (LIKE)", NULL);
-    psMetadataAddStr(pendingArgs, PS_LIST_TAIL, "-label", 0, "search on label", NULL);
+    psMetadataAddStr(pendingArgs, PS_LIST_TAIL, "-label", PS_META_DUPLICATE_OK, "search on label", NULL);
     psMetadataAddBool(pendingArgs, PS_LIST_TAIL, "-simple",  0, "use simple output format?", false);
     psMetadataAddU64(pendingArgs, PS_LIST_TAIL, "-limit",  0, "limit result set", 0);
Index: /branches/pap/ippTools/src/pxadd.c
===================================================================
--- /branches/pap/ippTools/src/pxadd.c	(revision 28483)
+++ /branches/pap/ippTools/src/pxadd.c	(revision 28484)
@@ -130,5 +130,8 @@
                        char *dvodb,
                        char *note,
-                       bool image_only)
+                       bool image_only,
+		       bool minidvodb,
+		       char *minidvodb_group,
+		       char *minidvodb_name)
 {
     PS_ASSERT_PTR_NON_NULL(config, false);
@@ -158,7 +161,10 @@
                          note     ? note     : "NULL",
                          image_only,
+			 minidvodb,
+			 minidvodb_group,
+			 minidvodb_name,
                          (long long) cam_id
     )) {
-        psError(PS_ERR_UNKNOWN, false, "database error");
+      psError(PS_ERR_UNKNOWN, false, "database error %s", query);
         return false;
     }
Index: /branches/pap/ippTools/src/pxadd.h
===================================================================
--- /branches/pap/ippTools/src/pxadd.h	(revision 28483)
+++ /branches/pap/ippTools/src/pxadd.h	(revision 28484)
@@ -38,5 +38,8 @@
 		       char *dvodb,
 		       char *note,
-		       bool image_only);
+		       bool image_only,
+		       bool minidvodb,
+		       char *minidvodb_group,
+		       char *minidvodb_name);
 
 #endif // PXADD_H
Index: /branches/pap/ippTools/src/pxchip.c
===================================================================
--- /branches/pap/ippTools/src/pxchip.c	(revision 28483)
+++ /branches/pap/ippTools/src/pxchip.c	(revision 28484)
@@ -40,5 +40,5 @@
     psMetadataAddStr(md,  PS_LIST_TAIL, "-telescope",          0, "search for telescope", NULL);
     psMetadataAddTime(md, PS_LIST_TAIL, "-dateobs_begin",      0, "search for exposures by time (>=)", NULL);
-    psMetadataAddTime(md, PS_LIST_TAIL, "-dateobs_end",        0, "search for exposures by time (<)", NULL);
+    psMetadataAddTime(md, PS_LIST_TAIL, "-dateobs_end",        0, "search for exposures by time (<=)", NULL);
     psMetadataAddStr(md,  PS_LIST_TAIL, "-exp_tag",            0, "search by exp_tag", NULL);
     psMetadataAddStr(md,  PS_LIST_TAIL, "-exp_type",           0, "search by exp_type", NULL);
Index: /branches/pap/ippTools/src/pxtools.c
===================================================================
--- /branches/pap/ippTools/src/pxtools.c	(revision 28483)
+++ /branches/pap/ippTools/src/pxtools.c	(revision 28484)
@@ -260,10 +260,11 @@
         psMetadataItem *item = NULL;
         while ((item = psListGetAndIncrement(iter))) {
+            psMetadataItem *new = psMetadataItemCopy(item);
             // need to change the name and comment
-            psFree (item->name);
-            item->name = psStringCopy (field);
-            psFree (item->comment);
-            item->comment = psStringCopy (op);
-            if (!psMetadataAddItem(where, item, PS_LIST_TAIL, PS_META_DUPLICATE_OK)) {
+            psFree (new->name);
+            new->name = psStringCopy (field);
+            psFree (new->comment);
+            new->comment = psStringCopy (op);
+            if (!psMetadataAddItem(where, new, PS_LIST_TAIL, PS_META_DUPLICATE_OK)) {
                 psError(psErrorCodeLast(), false, "failed to add item %s", field);
                 psFree(where);
@@ -271,5 +272,5 @@
             }
         }
-        psFree (iter);
+        psFree(iter);
     }
     return true;
@@ -298,4 +299,14 @@
     PXOPT_LOOKUP_STR(data_group, config->args,  "-set_data_group", false, false);
     PXOPT_LOOKUP_STR(note, config->args,        "-set_note", false, false);
+
+    if ((state)&&(!strcmp(state, "update"))) {
+        fprintf(stderr, "'-updaterun -set_state update' is not supported.");
+        if (!strcmp(runTable, "chipRun")) {
+            fprintf(stderr, " Use -setimfiletoupdate.\n");
+        } else {
+            fprintf(stderr, " Use -setskyfiletoupdate.\n");
+        }
+        exit(1);
+    }
 
     psString dist_group = NULL;
Index: /branches/pap/ippTools/src/stacktool.c
===================================================================
--- /branches/pap/ippTools/src/stacktool.c	(revision 28483)
+++ /branches/pap/ippTools/src/stacktool.c	(revision 28484)
@@ -39,5 +39,8 @@
 static bool addsumskyfileMode(pxConfig *config);
 static bool sumskyfileMode(pxConfig *config);
+static bool sassskyfileMode(pxConfig *config);
 static bool revertsumskyfileMode(pxConfig *config);
+static bool tosummaryMode(pxConfig *config);
+static bool addsummaryMode(pxConfig *config);
 static bool pendingcleanuprunMode(pxConfig *config);
 static bool pendingcleanupskyfileMode(pxConfig *config);
@@ -75,5 +78,8 @@
         MODECASE(STACKTOOL_MODE_ADDSUMSKYFILE,         addsumskyfileMode);
         MODECASE(STACKTOOL_MODE_SUMSKYFILE,            sumskyfileMode);
+	MODECASE(STACKTOOL_MODE_SASSSKYFILE,            sassskyfileMode);
         MODECASE(STACKTOOL_MODE_REVERTSUMSKYFILE,      revertsumskyfileMode);
+	MODECASE(STACKTOOL_MODE_TOSUMMARY,             tosummaryMode);
+	MODECASE(STACKTOOL_MODE_ADDSUMMARY,            addsummaryMode);
         MODECASE(STACKTOOL_MODE_PENDINGCLEANUPRUN,     pendingcleanuprunMode);
         MODECASE(STACKTOOL_MODE_PENDINGCLEANUPSKYFILE, pendingcleanupskyfileMode);
@@ -102,4 +108,84 @@
     exit(exit_status);
 }
+//stackAssociationRow *association = pxStackAssociationDefine(data_group,tess_id,filter,skycell_id);
+stackAssociationRow *pxStackAssociationDefine(pxConfig *config, psS64 stack_id) {
+  psString select = pxDataGet("stacktool_associationdefine_select.sql");
+  if (!select) {
+    psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+    return false;
+  }
+
+  psString idString = NULL;
+  psStringAppend(&idString, "%" PRId64, stack_id);
+  // Copy string to get around the issue with psStringSubstitute not believing
+  // that select is a psString.
+  psString rep = psStringCopy(select);
+  psFree(select);
+  select = rep;
+  psStringSubstitute(&select, idString, "@STACK_ID@");
+  psFree(idString);
+
+  if (!p_psDBRunQuery(config->dbh, select)) {
+    psError(PS_ERR_UNKNOWN,false, "database error");
+    psFree(select);
+    return(NULL);
+  }
+  psFree(select);
+  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(NULL);
+  }
+  if (psArrayLength(output) != 1) {
+    psWarning("stacktool: incorrect number of rows found");
+    psFree(output);
+    return(NULL);
+  }
+  psMetadata *outrow = psMetadataAlloc();
+  for (long i = 0; i < output->n; i++) {
+    psMetadata *row = output->data[i];
+
+    printf("%" PRId64 " %s %s %s %s\n",psMetadataLookupS64(NULL,row,"sass_id"),
+	   psMetadataLookupStr(NULL,row,"data_group"),
+	   psMetadataLookupStr(NULL,row,"tess_id"),
+	   psMetadataLookupStr(NULL,row,"filter"),
+	   psMetadataLookupStr(NULL,row,"projection_cell"));
+
+    if (psMetadataLookupS64(NULL,row,"sass_id") == PS_MAX_S64) {
+      psMetadataAddS64(outrow,PS_LIST_TAIL,"sass_id",PS_META_REPLACE,"",0);
+    }
+    else {
+      psMetadataAddS64(outrow,PS_LIST_TAIL,"sass_id",PS_META_REPLACE,"",psMetadataLookupS64(NULL,row,"sass_id"));
+    }
+    psMetadataAddStr(outrow,PS_LIST_TAIL,"data_group",PS_META_REPLACE,"",psMetadataLookupStr(NULL,row,"data_group"));
+    psMetadataAddStr(outrow,PS_LIST_TAIL,"tess_id",PS_META_REPLACE,"",psMetadataLookupStr(NULL,row,"tess_id"));
+    psMetadataAddStr(outrow,PS_LIST_TAIL,"filter",PS_META_REPLACE,"",psMetadataLookupStr(NULL,row,"filter"));
+    psMetadataAddStr(outrow,PS_LIST_TAIL,"projection_cell",PS_META_REPLACE,"",
+		     psMetadataLookupStr(NULL,row,"projection_cell"));
+  }
+  printf("%" PRId64 " %s %s %s %s\n",psMetadataLookupS64(NULL,outrow,"sass_id"),
+	 psMetadataLookupStr(NULL,outrow,"data_group"),
+	 psMetadataLookupStr(NULL,outrow,"tess_id"),
+	 psMetadataLookupStr(NULL,outrow,"filter"),
+	 psMetadataLookupStr(NULL,outrow,"projection_cell"));
+  
+
+  psFree(output);
+  stackAssociationRow *sassRow = stackAssociationObjectFromMetadata(outrow);
+  psFree(outrow);
+  return(sassRow);
+}
+      
+      
+  
+					      
 
 
@@ -151,4 +237,6 @@
     PXOPT_COPY_F32(config->args,  where, "-select_iq_m4_min",          "camProcessedExp.iq_m4", ">=");
     PXOPT_COPY_F32(config->args,  where, "-select_iq_m4_max",          "camProcessedExp.iq_m4", "<=");
+    PXOPT_COPY_F32(config->args,  where, "-select_zpt_obs_min",        "camProcessedExp.zpt_obs", ">=");
+    PXOPT_COPY_F32(config->args,  where, "-select_zpt_obs_max",        "camProcessedExp.zpt_obs", "<=");
 
     PXOPT_COPY_STR(config->args,  where, "-select_exp_type",           "rawExp.exp_type", "==");
@@ -370,5 +458,5 @@
             tess_id,
             filter,
-	    NULL, // software_ver
+            NULL, // software_ver
             note);
 
@@ -395,4 +483,48 @@
         psFree(run);
 
+	//CZW Add an association entry here.
+	// Define the requested association, and insert it if it doesn't already exist
+	stackAssociationRow *association = pxStackAssociationDefine(config,stack_id);
+	psS64 sass_id;
+	if (!association->sass_id) {
+	  psTrace("stacktool.association",2,"No required Association found. Adding.");
+
+	  if (!stackAssociationInsertObject(config->dbh,association)) {
+	    if (!psDBRollback(config->dbh)) {
+	      psError(PS_ERR_UNKNOWN, false, "database error");
+	    }
+	    psError(PS_ERR_UNKNOWN, false, "database error");
+	    psFree(output);
+	    psFree(run);
+	    psFree(insert);
+	    psFree(list);
+	    psFree(association);
+	    if (!psDBRollback(config->dbh)) {
+	      psError(PS_ERR_UNKNOWN, false, "database error");
+	    }
+	    return(false);
+	  }
+	  sass_id = psDBLastInsertID(config->dbh);
+	  association->sass_id = sass_id;
+	}
+	// Insert the map entry for this row.
+	stackAssociationMapRow *maprow = stackAssociationMapRowAlloc(sass_id,stack_id);
+	if (!stackAssociationMapInsertObject(config->dbh,maprow)) {
+	  if (!psDBRollback(config->dbh)) {
+	    psError(PS_ERR_UNKNOWN, false, "database error");
+	  }
+	  psError(PS_ERR_UNKNOWN, false, "database error");
+	  psFree(output);
+	  psFree(run);
+	  psFree(insert);
+	  psFree(list);
+	  psFree(association);
+	  if (!psDBRollback(config->dbh)) {
+	    psError(PS_ERR_UNKNOWN, false, "database error");
+	  }
+	  return(false);
+	}
+	
+	
         // Create a suitable insertion query for this run
         psString thisInsert = psStringCopy(insert);
@@ -527,5 +659,5 @@
         tess_id,
         filter,
-	NULL, // software_ver
+        NULL, // software_ver
         note);
 
@@ -552,4 +684,6 @@
     run->stack_id = psDBLastInsertID(config->dbh);
 
+    //CZW Add an association entry here.
+    
     // insert the stackInputSkyfile rows
     psListIterator *iter = psListIteratorAlloc(warp_ids->data.list, 0, false);
@@ -603,7 +737,8 @@
 #endif
     psMetadata *where = psMetadataAlloc();
-    PXOPT_COPY_S64(config->args, where, "-stack_id",  "stack_id",   "==");
-    PXOPT_COPY_STR(config->args, where, "-label",     "label",     "==");
-    PXOPT_COPY_STR(config->args, where, "-state",     "state",     "==");
+    PXOPT_COPY_S64(config->args, where, "-stack_id",  "stackRun.stack_id",   "==");
+    PXOPT_COPY_STR(config->args, where, "-label",     "stackRun.label",     "==");
+    PXOPT_COPY_STR(config->args, where, "-state",     "stackRun.state",     "==");
+    PXOPT_COPY_STR(config->args, where, "-sass_id",   "stackAssociationMap.sass_id",  "==");
     if (!psListLength(where->list)) {
         psFree(where);
@@ -612,4 +747,5 @@
     }
 
+    //CZW join against stackAssociationMap
     psString query = psStringCopy("UPDATE stackRun");
 
@@ -854,5 +990,5 @@
 
     psTrace("czw.test",1,"Received versions: pslib %s psmodules %s psphot %s ppstats %s ppstack %s streaks %s\n",
-	    ver_pslib,ver_psmodules,ver_psphot,ver_ppstats,ver_ppstack,ver_streaks);
+            ver_pslib,ver_psmodules,ver_psphot,ver_ppstats,ver_ppstack,ver_streaks);
     psString software_ver = NULL;
     if ((ver_pslib)&&(ver_psmodules)) {
@@ -871,5 +1007,5 @@
       software_ver = pxMergeCodeVersions(software_ver,ver_streaks);
     }
-    
+
     // default values
     PXOPT_LOOKUP_S16(fault, config->args, "-fault", false, false);
@@ -911,5 +1047,5 @@
                                good_frac,
                                fault,
-			       software_ver,
+                               software_ver,
                                quality
           )) {
@@ -922,13 +1058,13 @@
 
     if (fault == 0) {
-	// Set stackRun software if we are finished.
-	if (!pxSetRunSoftware(config, "stackRun", "stack_id", stack_id, software_ver)) {
-	  if (!psDBRollback(config->dbh)) {
-	    psError(PS_ERR_UNKNOWN, false, "database error");
-	  }
-	  psError(PS_ERR_UNKNOWN, false, "failed to set stackRun.software_ver for stack_id: %" PRId64,
-		  stack_id);
-	  return(false);
-	}
+        // Set stackRun software if we are finished.
+        if (!pxSetRunSoftware(config, "stackRun", "stack_id", stack_id, software_ver)) {
+          if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+          }
+          psError(PS_ERR_UNKNOWN, false, "failed to set stackRun.software_ver for stack_id: %" PRId64,
+                  stack_id);
+          return(false);
+        }
 
         if (!setstackRunState(config, stack_id, "full")) {
@@ -1038,23 +1174,27 @@
 }
 
-
-static bool revertsumskyfileMode(pxConfig *config)
+static bool sassskyfileMode(pxConfig *config)
 {
     PS_ASSERT_PTR_NON_NULL(config, false);
 
     psMetadata *where = psMetadataAlloc();
-    PXOPT_COPY_S64(config->args, where, "-stack_id", "stackSumSkyfile.stack_id", "==");
-    pxAddLabelSearchArgs(config, where, "-label", "stackRun.label", "==");
-    PXOPT_COPY_S16(config->args, where, "-fault", "stackSumSkyfile.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("stacktool_revertsumskyfile_delete.sql");
-    if (!delete) {
+    PXOPT_COPY_S64(config->args, where, "-sass_id", "stackAssociation.sass_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-tess_id", "stackAssociation.tess_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-projection_cell", "stackAssociation.projection_cell", "==");
+    PXOPT_COPY_STR(config->args, where, "-filter", "stackAssociation.filter", "LIKE");
+    PXOPT_COPY_STR(config->args, where, "-data_group", "stackAssociation.data_group", "LIKE");
+
+//  The following three selectors are incompatible with the sql so omit them
+//    PXOPT_COPY_S64(config->args, where, "-warp_id", "warpRun.warp_id", "==");
+//     PXOPT_COPY_S64(config->args, where, "-exp_id", "rawExp.exp_id", "==");
+//    PXOPT_COPY_STR(config->args, where, "-exp_name", "rawExp.exp_name", "==");
+
+    PXOPT_LOOKUP_BOOL(all, config->args, "-all", false);
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psString query = pxDataGet("stacktool_sassskyfile.sql");
+    if (!query) {
         psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
         return false;
@@ -1063,88 +1203,11 @@
     if (psListLength(where->list)) {
         psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
-        psStringAppend(&delete, " AND %s", whereClause);
+        psStringAppend(&query, " WHERE %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("stacktool", PS_LOG_INFO, "Deleted %d rows", numRows);
-
-    psFree(where);
-
-    return true;
-}
-
-
-static bool setstackRunState(pxConfig *config, psS64 stack_id, const char *state)
-{
-    PS_ASSERT_PTR_NON_NULL(state, false);
-
-    // check that state is a valid string value
-    if (!pxIsValidState(state)) {
-        psError(PS_ERR_UNKNOWN, false, "invalid stackRun state: %s", state);
-        return false;
-    }
-
-    char *query = "UPDATE stackRun SET state = '%s' WHERE stack_id = %"PRId64;
-    if (!p_psDBRunQueryF(config->dbh, query, state, stack_id)) {
-        psError(PS_ERR_UNKNOWN, false,
-                "failed to change state for stack_id %"PRId64, stack_id);
-        return false;
-    }
-
-    return true;
-}
-
-#ifdef notdef
-static bool setstackRunStateByLabel(pxConfig *config, const char *label, const char *state)
-{
-    PS_ASSERT_PTR_NON_NULL(state, false);
-
-    // check that state is a valid string value
-    if (!pxIsValidState(state)) {
-        psError(PS_ERR_UNKNOWN, false, "invalid stackRun state: %s", state);
-        return false;
-    }
-
-    char *query = "UPDATE stackRun SET state = '%s' WHERE label = '%s'";
-    if (!p_psDBRunQueryF(config->dbh, query, state, label)) {
-        psError(PS_ERR_UNKNOWN, false,
-                "failed to change state for label %s", label);
-        return false;
-    }
-
-    return true;
-}
-#endif
-
-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", "stackRun.label", "==");
-
-    psString query = pxDataGet("stacktool_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);
-    }
+    } else if (!all) {
+        psError(PXTOOLS_ERR_CONFIG, true, "search parameters or -all are required");
+        return false;
+    }
+
     psFree(where);
 
@@ -1165,5 +1228,14 @@
     psArray *output = p_psDBFetchResult(config->dbh);
     if (!output) {
-        psError(PS_ERR_UNKNOWN, false, "database error");
+        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;
     }
@@ -1174,31 +1246,233 @@
     }
 
+    if (psArrayLength(output)) {
+        if (!ippdbPrintMetadatas(stdout, output, "stackSumSkyfile", !simple)) {
+            psError(PS_ERR_UNKNOWN, false, "failed to print array");
+            psFree(output);
+            return false;
+        }
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+
+static bool revertsumskyfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-stack_id", "stackSumSkyfile.stack_id", "==");
+    pxAddLabelSearchArgs(config, where, "-label", "stackRun.label", "==");
+    PXOPT_COPY_S16(config->args, where, "-fault", "stackSumSkyfile.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("stacktool_revertsumskyfile_delete.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("stacktool", PS_LOG_INFO, "Deleted %d rows", numRows);
+
+    psFree(where);
+
+    return true;
+}
+
+
+static bool setstackRunState(pxConfig *config, psS64 stack_id, const char *state)
+{
+    PS_ASSERT_PTR_NON_NULL(state, false);
+
+    // check that state is a valid string value
+    if (!pxIsValidState(state)) {
+        psError(PS_ERR_UNKNOWN, false, "invalid stackRun state: %s", state);
+        return false;
+    }
+
+    char *query = "UPDATE stackRun SET state = '%s' WHERE stack_id = %"PRId64;
+    if (!p_psDBRunQueryF(config->dbh, query, state, stack_id)) {
+        psError(PS_ERR_UNKNOWN, false,
+                "failed to change state for stack_id %"PRId64, stack_id);
+        return false;
+    }
+
+    return true;
+}
+
+#ifdef notdef
+static bool setstackRunStateByLabel(pxConfig *config, const char *label, const char *state)
+{
+    PS_ASSERT_PTR_NON_NULL(state, false);
+
+    // check that state is a valid string value
+    if (!pxIsValidState(state)) {
+        psError(PS_ERR_UNKNOWN, false, "invalid stackRun state: %s", state);
+        return false;
+    }
+
+    char *query = "UPDATE stackRun SET state = '%s' WHERE label = '%s'";
+    if (!p_psDBRunQueryF(config->dbh, query, state, label)) {
+        psError(PS_ERR_UNKNOWN, false,
+                "failed to change state for label %s", label);
+        return false;
+    }
+
+    return true;
+}
+#endif
+static bool tosummaryMode(pxConfig *config) {
+  PS_ASSERT_PTR_NON_NULL(config, NULL);
+  
+  psMetadata *where = psMetadataAlloc();
+  PXOPT_COPY_S64(config->args, where, "-stack_id",    "stackSumSkyfile.warp_id", "==");
+  PXOPT_COPY_S64(config->args, where, "-sass_id",     "stackAssociationMap.sass_id", "==");
+  PXOPT_COPY_STR(config->args, where, "-tess_id",    "stackSumSkyfile.tess_id", "==");
+  PXOPT_COPY_STR(config->args, where, "-state",      "stackRun.state", "==");
+  PXOPT_COPY_STR(config->args, where, "-filter",    "stackRun.filter", "LIKE");
+  pxAddLabelSearchArgs (config, where, "-label",   "stackRun.label", "LIKE");
+  pxAddLabelSearchArgs (config, where, "-data_group",   "stackRun.data_group", "LIKE");
+  
+  PXOPT_LOOKUP_BOOL(all, config->args, "-all", false);
+
+  PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+  PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+  
+  // find all rawImfiles matching the default query
+  psString query = pxDataGet("stacktool_tosummary.sql");
+  if (!query) {
+    psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+    return false;
+  }
+
+  // generate where strings for arguments that require extra processing
+  // beyond PXOPT_COPY*
+  if (psListLength(where->list)) {
+    psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+    psStringAppend(&query, " AND %s", whereClause);
+    psFree(whereClause);
+  } else if (!all) {
+    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("stacktool", PS_LOG_INFO, "no rows found");
+    psFree(output);
+    return true;
+  }
+  
+  if (psArrayLength(output)) {
     // negative simple so the default is true
-    if (!ippdbPrintMetadatas(stdout, output, "stackPendingCleanupRun", !simple)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to print array");
-        psFree(output);
-        return false;
-    }
-
-    psFree(output);
-
-    return true;
-}
-
-static bool pendingcleanupskyfileMode(pxConfig *config)
+    if (!ippdbPrintMetadatas(stdout, output, "stackRun", !simple)) {
+      psError(PS_ERR_UNKNOWN, false, "failed to print array");
+      psFree(output);
+      return false;
+    }
+  }
+  
+  psFree(output);
+  return(true);
+}
+static bool addsummaryMode(pxConfig *config) {
+  PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+  PXOPT_LOOKUP_S64(sass_id, config->args, "-sass_id", true, false);
+  PXOPT_LOOKUP_STR(projection_cell, config->args, "-projection_cell", true, false);
+  PXOPT_LOOKUP_STR(path_base, config->args, "-path_base", true, false);
+
+  psString query = pxDataGet("stacktool_addsummary.sql");
+  if (!query) {
+    psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+    return(false);
+  }
+  if (!p_psDBRunQueryF(config->dbh, query, sass_id, projection_cell, path_base)) {
+    psError(PS_ERR_UNKNOWN, false, "database error");
+    psFree(query);
+    return(false);
+  }
+  psS64 numUpdated = psDBAffectedRows(config->dbh);
+  
+  if (numUpdated != 1) {
+    psError(PS_ERR_UNKNOWN, false, "should have affected 1 row");
+    psFree(query);
+    return false;
+  }
+  
+  psFree(query);
+
+  // Print anything here?
+  
+  return(true);
+}
+
+static bool pendingcleanuprunMode(pxConfig *config)
 {
     PS_ASSERT_PTR_NON_NULL(config, NULL);
 
-    PXOPT_LOOKUP_S64(stack_id, config->args, "-stack_id", false, false);
     PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
     PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
 
     psMetadata *where = psMetadataAlloc();
-    if (stack_id) {
-        PXOPT_COPY_S64(config->args, where, "-stack_id", "stack_id", "==");
-    }
+    PXOPT_COPY_S64(config->args, where, "-stack_id", "stackRun.stack_id", "==");
+    PXOPT_COPY_S64(config->args, where, "-sass_id", "stackAssociationMap.sass_id", "==");
+    
     pxAddLabelSearchArgs (config, where, "-label", "stackRun.label", "==");
 
-    psString query = pxDataGet("stacktool_pendingcleanupskyfile.sql");
+    psString query = pxDataGet("stacktool_pendingcleanuprun.sql");
     if (!query) {
         psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
@@ -1239,5 +1513,5 @@
 
     // negative simple so the default is true
-    if (!ippdbPrintMetadatas(stdout, output, "stackPendingCleanupSkyfile", !simple)) {
+    if (!ippdbPrintMetadatas(stdout, output, "stackPendingCleanupRun", !simple)) {
         psError(PS_ERR_UNKNOWN, false, "failed to print array");
         psFree(output);
@@ -1250,16 +1524,19 @@
 }
 
-
-static bool donecleanupMode(pxConfig *config)
+static bool pendingcleanupskyfileMode(pxConfig *config)
 {
     PS_ASSERT_PTR_NON_NULL(config, NULL);
 
+    PXOPT_LOOKUP_S64(stack_id, config->args, "-stack_id", false, false);
     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("stacktool_donecleanup.sql");
+    if (stack_id) {
+        PXOPT_COPY_S64(config->args, where, "-stack_id", "stack_id", "==");
+    }
+    pxAddLabelSearchArgs (config, where, "-label", "stackRun.label", "==");
+
+    psString query = pxDataGet("stacktool_pendingcleanupskyfile.sql");
     if (!query) {
         psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
@@ -1300,4 +1577,66 @@
 
     // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "stackPendingCleanupSkyfile", !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", "==");
+    PXOPT_COPY_STR(config->args, where, "-sass_id", "sass_id", "==");
+
+    psString query = pxDataGet("stacktool_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("stacktool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
     if (!ippdbPrintMetadatas(stdout, output, "stackDoneCleanup", !simple)) {
         psError(PS_ERR_UNKNOWN, false, "failed to print array");
@@ -1329,4 +1668,5 @@
 }
 
+//CZW I have not added sass information to the export/import run modes yet.
 bool exportrunMode(pxConfig *config)
 {
Index: /branches/pap/ippTools/src/stacktool.h
===================================================================
--- /branches/pap/ippTools/src/stacktool.h	(revision 28483)
+++ /branches/pap/ippTools/src/stacktool.h	(revision 28484)
@@ -33,5 +33,8 @@
     STACKTOOL_MODE_ADDSUMSKYFILE,
     STACKTOOL_MODE_SUMSKYFILE,
+    STACKTOOL_MODE_SASSSKYFILE,
     STACKTOOL_MODE_REVERTSUMSKYFILE,
+    STACKTOOL_MODE_TOSUMMARY,
+    STACKTOOL_MODE_ADDSUMMARY,
     STACKTOOL_MODE_PENDINGCLEANUPRUN,
     STACKTOOL_MODE_PENDINGCLEANUPSKYFILE,
Index: /branches/pap/ippTools/src/stacktoolConfig.c
===================================================================
--- /branches/pap/ippTools/src/stacktoolConfig.c	(revision 28483)
+++ /branches/pap/ippTools/src/stacktoolConfig.c	(revision 28484)
@@ -88,4 +88,6 @@
     psMetadataAddF64(definebyqueryArgs, PS_LIST_TAIL, "-select_iq_m4_min", 0, "define min iq_m4", NAN);
     psMetadataAddF64(definebyqueryArgs, PS_LIST_TAIL, "-select_iq_m4_max", 0, "define max iq_m4", NAN);
+    psMetadataAddF64(definebyqueryArgs, PS_LIST_TAIL, "-select_zpt_obs_min", 0, "define min zero point", NAN);
+    psMetadataAddF64(definebyqueryArgs, PS_LIST_TAIL, "-select_zpt_obs_max", 0, "define max zero point", NAN);
     psMetadataAddS32(definebyqueryArgs, PS_LIST_TAIL, "-random", 0, "use this number of random elements", 0);
     psMetadataAddS32(definebyqueryArgs, PS_LIST_TAIL, "-min_num", 0, "minimum number of inputs", 0);
@@ -197,4 +199,16 @@
     psMetadataAddBool(sumskyfileArgs, PS_LIST_TAIL, "-all",  0,            "enable search without arguments", false);
 
+    // -sassskyfile
+    psMetadata *sassskyfileArgs = psMetadataAlloc();
+    psMetadataAddS64(sassskyfileArgs, PS_LIST_TAIL, "-sass_id", 0,           "search by stack association ID", 0);
+    psMetadataAddStr(sassskyfileArgs, PS_LIST_TAIL, "-tess_id", 0,            "search by tess ID", 0);
+    psMetadataAddStr(sassskyfileArgs, PS_LIST_TAIL, "-projection_cell", 0,         "search by projection cell", 0);
+
+    psMetadataAddStr(sassskyfileArgs, PS_LIST_TAIL, "-data_group", 0,        "search by stackAssociation.data_group (LIKE comparison)", NULL);
+    psMetadataAddStr(sassskyfileArgs, PS_LIST_TAIL, "-filter", 0,            "search by filter (LIKE comparison)", NULL);
+    psMetadataAddU64(sassskyfileArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+    psMetadataAddBool(sassskyfileArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+    psMetadataAddBool(sassskyfileArgs, PS_LIST_TAIL, "-all",  0,            "enable search without arguments", false);
+
     // -revertsumskyfile
     psMetadata *revertsumskyfileArgs= psMetadataAlloc();
@@ -204,4 +218,25 @@
     psMetadataAddBool(revertsumskyfileArgs, PS_LIST_TAIL, "-all",  0, "allow no search terms", 0);
 
+    // -tosummary
+    psMetadata *tosummaryArgs = psMetadataAlloc();
+    psMetadataAddS64(tosummaryArgs, PS_LIST_TAIL, "-stack_id", 0,  "search by stack ID", 0);
+    psMetadataAddS64(tosummaryArgs, PS_LIST_TAIL, "-sass_id", 0,  "search by stack association ID", 0);
+    psMetadataAddStr(tosummaryArgs, PS_LIST_TAIL, "-tess_id", 0,   "search by tessellation ID", NULL);
+    psMetadataAddStr(tosummaryArgs, PS_LIST_TAIL, "-state", 0,     "search by state", NULL);
+    psMetadataAddStr(tosummaryArgs, PS_LIST_TAIL, "-filter", 0,    "search by filter", NULL);
+    psMetadataAddStr(tosummaryArgs, PS_LIST_TAIL, "-label", PS_META_DUPLICATE_OK, "search by stackRun label (LIKE comparison)", NULL);
+    psMetadataAddStr(tosummaryArgs, PS_LIST_TAIL, "-data_group", PS_META_DUPLICATE_OK, "search by stackRun data_group (LIKE comparison)", NULL);
+    psMetadataAddStr(tosummaryArgs, PS_LIST_TAIL, "-dist_group", PS_META_DUPLICATE_OK, "search by stackRun dist_group (LIKE comparison)", NULL);
+
+    psMetadataAddBool(tosummaryArgs, PS_LIST_TAIL, "-all",  0,             "search without arguments", false);
+    psMetadataAddU64(tosummaryArgs, PS_LIST_TAIL,  "-limit",  0,            "limit result set to N items", 0);
+    psMetadataAddBool(tosummaryArgs, PS_LIST_TAIL, "-simple",  0,          "use the simple output format", false);
+
+    // -addsummary
+    psMetadata *addsummaryArgs = psMetadataAlloc();
+    psMetadataAddS64(addsummaryArgs, PS_LIST_TAIL, "-sass_id", 0,      "set stack Association ID", 0);
+    psMetadataAddStr(addsummaryArgs, PS_LIST_TAIL, "-projection_cell", 0, "set projection cell", NULL);
+    psMetadataAddStr(addsummaryArgs, PS_LIST_TAIL, "-path_base", 0,     "set summary path base", NULL);		     
+    
     // -pendingcleanuprun
     psMetadata *pendingcleanuprunArgs = psMetadataAlloc();
@@ -252,4 +287,5 @@
     PXOPT_ADD_MODE("-addsumskyfile",   "", STACKTOOL_MODE_ADDSUMSKYFILE,   addsumskyfileArgs);
     PXOPT_ADD_MODE("-sumskyfile",      "list results of stackRun", STACKTOOL_MODE_SUMSKYFILE,      sumskyfileArgs);
+    PXOPT_ADD_MODE("-sassskyfile",      "list results of stackAssociation", STACKTOOL_MODE_SASSSKYFILE,      sassskyfileArgs);
     PXOPT_ADD_MODE("-revertsumskyfile","", STACKTOOL_MODE_REVERTSUMSKYFILE,      revertsumskyfileArgs);
     PXOPT_ADD_MODE("-pendingcleanuprun",     "show runs that need to be cleaned up", STACKTOOL_MODE_PENDINGCLEANUPRUN,    pendingcleanuprunArgs);
@@ -257,4 +293,6 @@
     PXOPT_ADD_MODE("-donecleanup",           "show runs that have been cleaned",     STACKTOOL_MODE_DONECLEANUP,          donecleanupArgs);
     PXOPT_ADD_MODE("-updatesumskyfile",      "update fault code for sumskyfile",     STACKTOOL_MODE_UPDATESUMSKYFILE,          updatesumskyfileArgs);
+    PXOPT_ADD_MODE("-tosummary",            "show runs that can be summarized", STACKTOOL_MODE_TOSUMMARY, tosummaryArgs);
+    PXOPT_ADD_MODE("-addsummary",           "add entry to the summary table", STACKTOOL_MODE_ADDSUMMARY, addsummaryArgs);
     PXOPT_ADD_MODE("-exportrun",            "export run for import on other database", STACKTOOL_MODE_EXPORTRUN, exportrunArgs);
     PXOPT_ADD_MODE("-importrun",            "import run from metadata file",           STACKTOOL_MODE_IMPORTRUN, importrunArgs);
Index: /branches/pap/ippTools/src/warptool.c
===================================================================
--- /branches/pap/ippTools/src/warptool.c	(revision 28483)
+++ /branches/pap/ippTools/src/warptool.c	(revision 28484)
@@ -48,4 +48,6 @@
 static bool maskedMode(pxConfig *config);
 static bool unblockMode(pxConfig *config);
+static bool tosummaryMode(pxConfig *config);
+static bool addsummaryMode(pxConfig *config);
 static bool pendingcleanuprunMode(pxConfig *config);
 static bool pendingcleanupwarpMode(pxConfig *config);
@@ -101,4 +103,6 @@
         MODECASE(WARPTOOL_MODE_MASKED,             maskedMode);
         MODECASE(WARPTOOL_MODE_UNBLOCK,            unblockMode);
+	MODECASE(WARPTOOL_MODE_TOSUMMARY,          tosummaryMode);
+	MODECASE(WARPTOOL_MODE_ADDSUMMARY,         addsummaryMode);
         MODECASE(WARPTOOL_MODE_PENDINGCLEANUPRUN,  pendingcleanuprunMode);
         MODECASE(WARPTOOL_MODE_PENDINGCLEANUPSKYFILE, pendingcleanupwarpMode);
@@ -426,5 +430,5 @@
     }
 
-    psString query = psStringCopy("UPDATE warpRun JOIN warpSkyfile USING(warp_id) JOIN fakeRun USING(fake_id) JOIN camRun USING(cam_id) JOIN chipRun USING(chip_id) JOIN rawExp USING(exp_id)");
+    psString query = psStringCopy("UPDATE warpRun");
 
     // pxUpdateRun gets parameters from config->args and updates
@@ -1548,4 +1552,133 @@
 }
 
+static bool tosummaryMode(pxConfig *config) {
+  PS_ASSERT_PTR_NON_NULL(config, NULL);
+  
+  psMetadata *where = psMetadataAlloc();
+  PXOPT_COPY_S64(config->args, where, "-warp_id",    "warpSkyfile.warp_id", "==");
+  PXOPT_COPY_STR(config->args, where, "-tess_id",    "warpSkyfile.tess_id", "==");
+  PXOPT_COPY_STR(config->args, where, "-state",      "warpRun.state", "==");
+  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, "-fake_id",    "fakeRun.fake_id", "==");
+  PXOPT_COPY_TIME(config->args, where, "-dateobs_begin", "rawExp.dateobs",  ">=");
+  PXOPT_COPY_TIME(config->args, where, "-dateobs_end",   "rawExp.dateobs",  "<=");
+  PXOPT_COPY_STR(config->args, where, "-filter",    "rawExp.filter", "LIKE");
+  PXOPT_COPY_S64(config->args, where, "-magicked", "warpSkyfile.magicked", "==");
+  pxAddLabelSearchArgs (config, where, "-label",   "warpRun.label", "LIKE");
+  pxAddLabelSearchArgs (config, where, "-data_group",   "warpRun.data_group", "LIKE");
+
+  PXOPT_LOOKUP_BOOL(all, config->args, "-all", false);
+
+  PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+  PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+  
+  // find all rawImfiles matching the default query
+  psString query = pxDataGet("warptool_tosummary.sql");
+  if (!query) {
+    psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+    return false;
+  }
+
+  // generate where strings for arguments that require extra processing
+  // beyond PXOPT_COPY*
+  psString where2 = NULL;
+  if (!pxmagicAddWhere(config, &where2, "warpSkyfile")) {
+    psError(psErrorCodeLast(), false, "pxMagicAddWhere failed");
+    return false;
+  }
+  
+  if (psListLength(where->list)) {
+    psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+    psStringAppend(&query, " AND %s", whereClause);
+    psFree(whereClause);
+  } else if (!all && !where2) {
+    psError(PXTOOLS_ERR_CONFIG, true, "search parameters or -all are required");
+    return false;
+  }
+  
+  if (where2) {
+    psStringAppend(&query, " %s", where2);
+  }
+  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("warptool", PS_LOG_INFO, "no rows found");
+    psFree(output);
+    return true;
+  }
+  
+  if (psArrayLength(output)) {
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "warpRun", !simple)) {
+      psError(PS_ERR_UNKNOWN, false, "failed to print array");
+      psFree(output);
+      return false;
+    }
+  }
+  
+  psFree(output);
+  return(true);
+}
+static bool addsummaryMode(pxConfig *config) {
+  PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+  PXOPT_LOOKUP_S64(warp_id, config->args, "-warp_id", true, false);
+  PXOPT_LOOKUP_STR(projection_cell, config->args, "-projection_cell", true, false);
+  PXOPT_LOOKUP_STR(path_base, config->args, "-path_base", true, false);
+
+  psString query = pxDataGet("warptool_addsummary.sql");
+  if (!query) {
+    psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+    return(false);
+  }
+  if (!p_psDBRunQueryF(config->dbh, query, warp_id, projection_cell, path_base)) {
+    psError(PS_ERR_UNKNOWN, false, "database error");
+    psFree(query);
+    return(false);
+  }
+  psS64 numUpdated = psDBAffectedRows(config->dbh);
+  
+  if (numUpdated != 1) {
+    psError(PS_ERR_UNKNOWN, false, "should have affected 1 row");
+    psFree(query);
+    return false;
+  }
+  
+  psFree(query);
+
+  // Print anything here?
+  
+  return(true);
+}
+
 static bool pendingcleanuprunMode(pxConfig *config)
 {
Index: /branches/pap/ippTools/src/warptool.h
===================================================================
--- /branches/pap/ippTools/src/warptool.h	(revision 28483)
+++ /branches/pap/ippTools/src/warptool.h	(revision 28484)
@@ -44,4 +44,6 @@
     WARPTOOL_MODE_MASKED,
     WARPTOOL_MODE_UNBLOCK,
+    WARPTOOL_MODE_TOSUMMARY,
+    WARPTOOL_MODE_ADDSUMMARY,
     WARPTOOL_MODE_PENDINGCLEANUPRUN,
     WARPTOOL_MODE_PENDINGCLEANUPSKYFILE,
Index: /branches/pap/ippTools/src/warptoolConfig.c
===================================================================
--- /branches/pap/ippTools/src/warptoolConfig.c	(revision 28483)
+++ /branches/pap/ippTools/src/warptoolConfig.c	(revision 28484)
@@ -321,4 +321,32 @@
     psMetadataAddStr(unblockArgs, PS_LIST_TAIL, "-label",  0,            "name of a label to unmask (required)", NULL);
 
+    // -tosummary
+    psMetadata *tosummaryArgs = psMetadataAlloc();
+    psMetadataAddS64(tosummaryArgs, PS_LIST_TAIL, "-warp_id", 0,         "search by warp ID", 0);
+    psMetadataAddStr(tosummaryArgs, PS_LIST_TAIL,  "-tess_id",  0,          "search by tessellation ID", NULL);
+    psMetadataAddStr(tosummaryArgs, PS_LIST_TAIL, "-state",  0,            "search by state", NULL);
+    psMetadataAddS64(tosummaryArgs, PS_LIST_TAIL, "-exp_id", 0,            "search by exposure tag", 0);
+    psMetadataAddStr(tosummaryArgs, PS_LIST_TAIL, "-exp_name", 0,          "search by exposure tag", 0);
+    psMetadataAddS64(tosummaryArgs, PS_LIST_TAIL, "-fake_id", 0,           "search by phase 3 version of exposure tag", 0);
+    psMetadataAddTime(tosummaryArgs, PS_LIST_TAIL, "-dateobs_begin", 0,    "search for exposures by time (>=)", NULL);
+    psMetadataAddTime(tosummaryArgs, PS_LIST_TAIL, "-dateobs_end", 0,      "search for exposures by time (<=)", NULL);
+    psMetadataAddStr(tosummaryArgs, PS_LIST_TAIL,  "-filter", 0,           "search for exposures by filter", NULL);
+    psMetadataAddS64(tosummaryArgs, PS_LIST_TAIL,  "-magicked", 0,         "search by magic id", 0);
+    psMetadataAddStr(tosummaryArgs, PS_LIST_TAIL,  "-label",  PS_META_DUPLICATE_OK, "search by warpRun label", NULL);
+    psMetadataAddStr(tosummaryArgs, PS_LIST_TAIL,  "-data_group",  PS_META_DUPLICATE_OK, "search by warpRun data_group", NULL);
+    psMetadataAddStr(tosummaryArgs, PS_LIST_TAIL,  "-dist_group",  PS_META_DUPLICATE_OK, "search by warpRun dist_group", NULL);
+    psMetadataAddBool(tosummaryArgs, PS_LIST_TAIL, "-destreaked", 0, "search for runs that have been destreaked", false);
+    psMetadataAddBool(tosummaryArgs, PS_LIST_TAIL, "-not_destreaked", 0, "search for runs that have not been destreaked", false);
+    
+    psMetadataAddBool(tosummaryArgs, PS_LIST_TAIL, "-all",  0,             "search without arguments", false);
+    psMetadataAddU64(tosummaryArgs, PS_LIST_TAIL,  "-limit",  0,            "limit result set to N items", 0);
+    psMetadataAddBool(tosummaryArgs, PS_LIST_TAIL, "-simple",  0,          "use the simple output format", false);
+
+    // -addsummary
+    psMetadata *addsummaryArgs = psMetadataAlloc();
+    psMetadataAddS64(addsummaryArgs, PS_LIST_TAIL, "-warp_id", 0,         "set warp ID", 0);
+    psMetadataAddStr(addsummaryArgs, PS_LIST_TAIL, "-projection_cell", 0,   "set projection cell", NULL);
+    psMetadataAddStr(addsummaryArgs, PS_LIST_TAIL, "-path_base", 0,       "set summary path base", NULL);
+    
     // -pendingcleanuprun
     psMetadata *pendingcleanuprunArgs = psMetadataAlloc();
@@ -430,4 +458,7 @@
     PXOPT_ADD_MODE("-updateskyfile", "update fault code for skyfile", WARPTOOL_MODE_UPDATESKYFILE, updateskyfileArgs);
     PXOPT_ADD_MODE("-setskyfiletoupdate", "set cleaned skyfile to be updated", WARPTOOL_MODE_SETSKYFILETOUPDATE, setskyfiletoupdateArgs);
+    PXOPT_ADD_MODE("-tosummary",            "show runs that can be summarized", WARPTOOL_MODE_TOSUMMARY, tosummaryArgs);
+    PXOPT_ADD_MODE("-addsummary",           "add entry to the summary table", WARPTOOL_MODE_ADDSUMMARY, addsummaryArgs);
+
     PXOPT_ADD_MODE("-exportrun",            "export run for import on other database", WARPTOOL_MODE_EXPORTRUN, exportrunArgs);
     PXOPT_ADD_MODE("-importrun",            "import run from metadata file",           WARPTOOL_MODE_IMPORTRUN, importrunArgs);
Index: /branches/pap/ippconfig/gpc1/dvo.config
===================================================================
--- /branches/pap/ippconfig/gpc1/dvo.config	(revision 28483)
+++ /branches/pap/ippconfig/gpc1/dvo.config	(revision 28484)
@@ -17,6 +17,7 @@
 CCDNUM-KEYWORD		EXTNAME
 ST-KEYWORD		NONE
-OBSERVATORY-LATITUDE	NONE
-OBSERVATORY-LONGITUDE	NONE
+OBSERVATORY-LATITUDE	20.7070999146
+OBSERVATORY-LONGITUDE	10.4170608521
+
 SUBPIX_DATAFILE		NONE
 
Index: /branches/pap/ippconfig/gpc1/psastro.config
===================================================================
--- /branches/pap/ippconfig/gpc1/psastro.config	(revision 28483)
+++ /branches/pap/ippconfig/gpc1/psastro.config	(revision 28484)
@@ -245,2 +245,43 @@
 CROSSTALK_MAX_MAG                F32  20.0
 CROSSTALK_MASK                   BOOL TRUE
+
+PS1_REFERENCE METADATA
+  PSASTRO.CATDIR              STR      PS1.GRIZY
+  ## PHOTCODE.DATA MULTI UPDATE
+  ## PHOTCODE.DATA METADATA
+  ##   FILTER   STR g
+  ##   ZEROPT   F32 24.541
+  ##   PHOTCODE STR g
+  ##   GHOST_MAX_MAG                   F32 -20.0
+  ## END
+  ## PHOTCODE.DATA METADATA
+  ##   FILTER   STR r
+  ##   ZEROPT   F32 24.705.1
+  ##   PHOTCODE STR r
+  ##   GHOST_MAX_MAG                   F32 -20.0
+  ## END
+  ## PHOTCODE.DATA METADATA
+  ##   FILTER   STR i
+  ##   ZEROPT   F32 24.580
+  ##   PHOTCODE STR i
+  ##   GHOST_MAX_MAG                   F32 -25.0
+  ## END
+  ## PHOTCODE.DATA METADATA
+  ##   FILTER   STR z
+  ##   ZEROPT   F32 24.201
+  ##   PHOTCODE STR z
+  ##   GHOST_MAX_MAG                   F32 -25.0
+  ## END
+  ## PHOTCODE.DATA METADATA
+  ##   FILTER   STR y
+  ##   ZEROPT   F32 23.260
+  ##   PHOTCODE STR y
+  ##   GHOST_MAX_MAG                   F32 -25.0
+  ## END
+  ## PHOTCODE.DATA METADATA
+  ##   FILTER   STR w
+  ##   ZEROPT   F32 26.0
+  ##   PHOTCODE STR r
+  ##   GHOST_MAX_MAG                   F32 -20.0
+  ## END
+END
Index: /branches/pap/magic/Makefile
===================================================================
--- /branches/pap/magic/Makefile	(revision 28483)
+++ /branches/pap/magic/Makefile	(revision 28484)
@@ -12,5 +12,5 @@
 default: all
 
-all: qt-everywhere ssa-core-cpp magic verify
+all: ssa-core-cpp magic verify
 
 install: magic.install verify.install
@@ -24,7 +24,8 @@
 	tar xvzf $(MAGIC_DIR)/magic.tgz
 	tar xvzf $(MAGIC_DIR)/ssa-core-cpp.tgz
-	tar xvzf $(QTSRC)
 	cp Makefile.magic magic/Makefile.magic
 	cp magic/VerifyStreaks/main.cpp verify/VerifyStreaks.cpp
+
+# tar xvzf $(QTSRC)
 
 qt-everywhere: $(QTDIR)/lib/libQtCore.so
Index: /branches/pap/magic/remove/src/streaksio.c
===================================================================
--- /branches/pap/magic/remove/src/streaksio.c	(revision 28483)
+++ /branches/pap/magic/remove/src/streaksio.c	(revision 28484)
@@ -1294,5 +1294,12 @@
         }
     }
-    sfiles->maskMask = ~convPoor;
+    // preserve pixels that are only suspect
+    psU32 suspect = (double) psMetadataLookupU32(&status, masks, "SUSPECT");
+    if (!status) {
+        psError(PM_ERR_CONFIG, false, "failed to lookup mask value for SUSPECT in recipes\n");
+        streaksExit("", PS_EXIT_CONFIG_ERROR);
+    }
+
+    sfiles->maskMask = ~(convPoor | suspect);
 }
 
Index: /branches/pap/magic/remove/src/streaksremove.c
===================================================================
--- /branches/pap/magic/remove/src/streaksremove.c	(revision 28483)
+++ /branches/pap/magic/remove/src/streaksremove.c	(revision 28484)
@@ -940,4 +940,8 @@
 
         psArray *inTable = psFitsReadTable(in->fits);
+        if (!inTable) {
+            psErrorStackPrint(stderr, "failed to read tablle in %s", in->resolved_name);
+            streaksExit("", PS_EXIT_DATA_ERROR);
+        }
         if (!inTable->n) {
             psErrorStackPrint(stderr, "table in %s is empty", in->resolved_name);
Index: /branches/pap/magic/verify/Makefile
===================================================================
--- /branches/pap/magic/verify/Makefile	(revision 28483)
+++ /branches/pap/magic/verify/Makefile	(revision 28484)
@@ -4,4 +4,7 @@
 CXXFLAGS = -W -Wall -O3 -DNDEBUG
 # CXXFLAGS = -W -Wall -g
+
+QTLIB_INC = /home/panstarrs/ipp/src/qtlib/include
+QTLIB_LIB = /home/panstarrs/ipp/src/qtlib/lib
 
 SSA_SRC = ../ssa-core-cpp/src
@@ -12,6 +15,6 @@
 PSINC_DIR = $(PSCONFDIR)/$(PSCONFIG)/include
 
-CINC = -I. -I$(SSA_SRC) -I$(PSINC_DIR) -I$(MAGIC_SRC)
-CLIB = -L. -L$(SSA_LIB) -I$(PSINC_DIR) -lcfitsio -lSSA -lQtCore -lQtGui
+CINC = -I. -I$(QTLIB_INC) -I$(SSA_SRC) -I$(PSINC_DIR) -I$(MAGIC_SRC)
+CLIB = -L. -L$(QTLIB_LIB) -L$(SSA_LIB) -I$(PSINC_DIR) -lcfitsio -lSSA -lQtCore -lQtGui
 
 all : $(TARGETS)
Index: /branches/pap/ppArith/src/ppArith.h
===================================================================
--- /branches/pap/ppArith/src/ppArith.h	(revision 28483)
+++ /branches/pap/ppArith/src/ppArith.h	(revision 28484)
@@ -44,4 +44,5 @@
                     const pmReadout *input1, ///< Input readout
                     const pmReadout *input2, ///< Input readout
+                    float const2,            ///< Constant to apply
                     const pmConfig *config, ///< Configuration
                     const pmFPAview *view ///< View of readout on which to operate
Index: /branches/pap/ppArith/src/ppArithArguments.c
===================================================================
--- /branches/pap/ppArith/src/ppArithArguments.c	(revision 28483)
+++ /branches/pap/ppArith/src/ppArithArguments.c	(revision 28484)
@@ -51,5 +51,5 @@
                         )
 {
-    psString value = psMetadataLookupStr(NULL, arguments, argName); // Value of interest 
+    psString value = psMetadataLookupStr(NULL, arguments, argName); // Value of interest
     if (value && strlen(value) > 0) {
         return psMetadataAddStr(target, PS_LIST_TAIL, mdName, 0, NULL, value);
@@ -82,4 +82,5 @@
     psMetadataAddStr(arguments, PS_LIST_TAIL, "-op", 0, "Operation to perform", NULL);
     psMetadataAddStr(arguments, PS_LIST_TAIL, "-file2", 0, "Second image", NULL);
+    psMetadataAddF32(arguments, PS_LIST_TAIL, "-const2", 0, "Constant", NAN);
     psMetadataAddBool(arguments, PS_LIST_TAIL, "-mask", 0, "Treat images as masks", false);
     psMetadataAddStr(arguments, PS_LIST_TAIL, "-stats", 0, "Statistics file", NULL);
@@ -98,5 +99,5 @@
                      "File rule for output", outFilerule);
 
-    bool status = false;                // Status for file definition 
+    bool status = false;                // Status for file definition
 
     // First file
@@ -135,4 +136,12 @@
         }
     }
+    float const2 = psMetadataLookupF32(NULL, arguments, "-const2"); // Constant to apply
+    if (!isnan(const2)) {
+        if (name2) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Cannot specify both -file2 and -const2");
+            goto ERROR;
+        }
+        psMetadataAddF32(config->arguments, PS_LIST_TAIL, "PPARITH.CONST", 0, "Constant to apply", const2);
+    }
 
     // Output image
Index: /branches/pap/ppArith/src/ppArithLoop.c
===================================================================
--- /branches/pap/ppArith/src/ppArithLoop.c	(revision 28483)
+++ /branches/pap/ppArith/src/ppArithLoop.c	(revision 28484)
@@ -62,4 +62,6 @@
     }
     psFree(iter);
+
+    float const2 = psMetadataLookupF32(&mdok, config->arguments, "PPARITH.CONST");
 
     pmFPAview *view = pmFPAviewAlloc(0); // Pointer into FPA hierarchy
@@ -138,5 +140,5 @@
 
                 // Perform the analysis
-                if (!ppArithReadout(outRO, inRO1, inRO2, config, view)) {
+                if (!ppArithReadout(outRO, inRO1, inRO2, const2, config, view)) {
                     psError(PS_ERR_UNKNOWN, false, "Unable to perform arithmetic.\n");
                     return false;
Index: /branches/pap/ppArith/src/ppArithReadout.c
===================================================================
--- /branches/pap/ppArith/src/ppArithReadout.c	(revision 28483)
+++ /branches/pap/ppArith/src/ppArithReadout.c	(revision 28484)
@@ -21,5 +21,5 @@
 #include "ppArith.h"
 
-bool ppArithReadout(pmReadout *output, const pmReadout *input1, const pmReadout *input2,
+bool ppArithReadout(pmReadout *output, const pmReadout *input1, const pmReadout *input2, float const2,
                     const pmConfig *config, const pmFPAview *view)
 {
@@ -77,4 +77,6 @@
     if (input2) {
         psBinaryOp(outImage, inImage1, op, inImage2);
+    }  else if (!isnan(const2)) {
+        psBinaryOp(outImage, inImage1, op, psScalarAlloc(const2, inImage1->type.type));
     } else {
         psUnaryOp(outImage, inImage1, op);
Index: /branches/pap/ppImage/src/ppImageLoop.c
===================================================================
--- /branches/pap/ppImage/src/ppImageLoop.c	(revision 28483)
+++ /branches/pap/ppImage/src/ppImageLoop.c	(revision 28484)
@@ -175,4 +175,5 @@
             ESCAPE("Unable to measures pixel stats for image");
         }
+
         if (!ppImageMosaicChip(config, options, view, "PPIMAGE.CHIP", "PPIMAGE.OUTPUT")) {
             ESCAPE("Unable to mosaic chip");
@@ -181,4 +182,5 @@
 
         // we perform photometry on the readouts of this chip in the output
+
         psTimerStart(TIMER_PHOT);
         if (options->doPhotom) {
Index: /branches/pap/ppImage/src/ppImageMosaic.c
===================================================================
--- /branches/pap/ppImage/src/ppImageMosaic.c	(revision 28483)
+++ /branches/pap/ppImage/src/ppImageMosaic.c	(revision 28484)
@@ -28,4 +28,20 @@
     }
 
+    // If the input is a dark (normalising and mosaicking) then it looks like a video cell and will be ignored
+    // by pmChipMosaic.  We therefore hack the structure so it doesn't look like a video cell.
+    psVector *darkNumbers = NULL;       // Number of dark readouts for each cell
+    if (psMetadataLookupBool(&status, config->arguments, "INPUT_IS_DARK")) {
+        darkNumbers = psVectorAlloc(inChip->cells->n, PS_TYPE_S32);
+	psVectorInit(darkNumbers, 0);
+        for (int i = 0; i < inChip->cells->n; i++) {
+            pmCell *cell = inChip->cells->data[i];
+            if (!cell) {
+                continue;
+            }
+            darkNumbers->data.S32[i] = cell->readouts->n;
+            cell->readouts->n = 1;
+        }
+    }
+
     psTrace("pmChipMosaic", 5, "mosaic chip %s to %s (xbin,ybin: %d,%d to %d,%d)\n",
             in->name, out->name, in->xBin, in->yBin, out->xBin, out->yBin);
@@ -34,4 +50,17 @@
     // image products pure trimmed images, but also increases the memory footprint.
     status = pmChipMosaic(outChip, inChip, true, options->blankMask);
+
+    // Restore dark structure
+    if (darkNumbers) {
+        for (int i = 0; i < inChip->cells->n; i++) {
+            pmCell *cell = inChip->cells->data[i];
+            if (!cell) {
+                continue;
+            }
+            cell->readouts->n = darkNumbers->data.S32[i];
+        }
+        psFree(darkNumbers);
+    }
+
     return status;
 }
Index: /branches/pap/ppImage/src/ppImagePhotom.c
===================================================================
--- /branches/pap/ppImage/src/ppImagePhotom.c	(revision 28483)
+++ /branches/pap/ppImage/src/ppImagePhotom.c	(revision 28484)
@@ -11,7 +11,7 @@
     pmCell *cell;
     pmReadout *readout;
-
+    printf("%x %s\n",psErrorCodeLast(),psErrorCodeString(psErrorCodeLast()));
     psphotInit();
-
+    printf("%x %s\n",psErrorCodeLast(),psErrorCodeString(psErrorCodeLast()));
     // find or define a pmFPAfile PSPHOT.INPUT
     pmFPAfile *input = psMetadataLookupPtr (&status, config->files, "PSPHOT.INPUT");
@@ -20,10 +20,10 @@
         return false;
     }
-
+    printf("%x %s\n",psErrorCodeLast(),psErrorCodeString(psErrorCodeLast()));
     // we make a new copy of the output chip to keep psphot from modifying the output image
     pmChip *oldChip = pmFPAviewThisChip (view, input->src);
     pmChip *newChip = pmFPAviewThisChip (view, input->fpa);
     pmChipCopy (newChip, oldChip);
-
+    printf("%x %s\n",psErrorCodeLast(),psErrorCodeString(psErrorCodeLast()));
     // iterate over the cells and readout for this chip
     while ((cell = pmFPAviewNextCell (view, input->fpa, 1)) != NULL) {
@@ -34,5 +34,5 @@
         while ((readout = pmFPAviewNextReadout (view, input->fpa, 1)) != NULL) {
             if (! readout->data_exists) { continue; }
-
+	    printf("%x %s\n",psErrorCodeLast(),psErrorCodeString(psErrorCodeLast()));
             // run the actual photometry analysis
             if (!psphotReadout (config, view)) {
Index: /branches/pap/ppSkycell/src/ppSkycell.h
===================================================================
--- /branches/pap/ppSkycell/src/ppSkycell.h	(revision 28483)
+++ /branches/pap/ppSkycell/src/ppSkycell.h	(revision 28484)
@@ -16,4 +16,5 @@
     int bin1, bin2;                     // Binning factors
     pmConfig *config;                   // Configuration
+  bool doFits;                          // hold whether to do fits as well.
 } ppSkycellData;
 
Index: /branches/pap/ppSkycell/src/ppSkycellCamera.c
===================================================================
--- /branches/pap/ppSkycell/src/ppSkycellCamera.c	(revision 28483)
+++ /branches/pap/ppSkycell/src/ppSkycellCamera.c	(revision 28484)
@@ -103,4 +103,12 @@
         return false;
     }
+    if (!pmFPAfileDefineOutput(data->config, NULL, "PPSKYCELL.BIN1")) {
+        psError(psErrorCodeLast(), false, "Unable to define output.");
+        return false;
+    }
+    if (!pmFPAfileDefineOutput(data->config, NULL, "PPSKYCELL.BIN2")) {
+        psError(psErrorCodeLast(), false, "Unable to define output.");
+        return false;
+    }
 
     // Now the camera has been determined, we can read the recipe
@@ -116,4 +124,6 @@
     data->bin2 = psMetadataLookupS32(NULL, recipe, "BIN2");
 
+    data->doFits = psMetadataLookupBool(NULL, recipe, "MAKEFITS");
+    
     if (data->bin1 <= 0 || data->bin2 <= 0) {
         psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Unable to find legitimate values for BIN1 and BIN2");
Index: /branches/pap/ppSkycell/src/ppSkycellLoop.c
===================================================================
--- /branches/pap/ppSkycell/src/ppSkycellLoop.c	(revision 28483)
+++ /branches/pap/ppSkycell/src/ppSkycellLoop.c	(revision 28484)
@@ -284,9 +284,15 @@
         pmFPAfileActivate(data->config->files, true, "PPSKYCELL.JPEG1");
         pmFPAfileActivate(data->config->files, true, "PPSKYCELL.JPEG2");
+	if (data->doFits) {
+	  pmFPAfileActivate(data->config->files, true, "PPSKYCELL.BIN1");
+	  pmFPAfileActivate(data->config->files, true, "PPSKYCELL.BIN2");
+	}
+	
         pmFPAview *view = filesIterateDown(data->config); // View to readout
 
+	
         pmCell *cell1 = pmFPAfileThisCell(data->config->files, view, "PPSKYCELL.JPEG1"); // Rebinned cell 1
         pmCell *cell2 = pmFPAfileThisCell(data->config->files, view, "PPSKYCELL.JPEG2"); // Rebinned cell 2
-        psFree(view);
+
         pmReadout *ro1 = pmReadoutAlloc(cell1), *ro2 = pmReadoutAlloc(cell2); // Binned readouts
 
@@ -298,11 +304,48 @@
         ro1->data_exists = cell1->data_exists = cell1->parent->data_exists = true;
         ro2->data_exists = cell2->data_exists = cell2->parent->data_exists = true;
-
+	
         pmFPAfile *file1 = pmFPAfileSelectSingle(data->config->files, "PPSKYCELL.JPEG1", 0);
         file1->save = true;
-        file1->index = i;
+        file1->fileIndex = i;
         pmFPAfile *file2 = pmFPAfileSelectSingle(data->config->files, "PPSKYCELL.JPEG2", 0);
         file2->save = true;
-        file2->index = i;
+        file2->fileIndex = i;
+
+	if (data->doFits) {
+	  pmCell *Fcell1 = pmFPAfileThisCell(data->config->files, view, "PPSKYCELL.BIN1"); // Rebinned cell 1
+	  pmCell *Fcell2 = pmFPAfileThisCell(data->config->files, view, "PPSKYCELL.BIN2"); // Rebinned cell 2
+
+	  // This is a hack to get a functioning header created so the fits images can be written out.
+	  psMetadataAddS32(Fcell1->concepts,PS_LIST_TAIL,"CELL.XPARITY", PS_META_REPLACE,"",1);
+	  psMetadataAddS32(Fcell1->concepts,PS_LIST_TAIL,"CELL.YPARITY", PS_META_REPLACE,"",1);
+	  psMetadataAddS32(Fcell1->concepts,PS_LIST_TAIL,"CELL.READDIR", PS_META_REPLACE,"",1);
+	  psMetadataAddS32(Fcell2->concepts,PS_LIST_TAIL,"CELL.XPARITY", PS_META_REPLACE,"",1);
+	  psMetadataAddS32(Fcell2->concepts,PS_LIST_TAIL,"CELL.YPARITY", PS_META_REPLACE,"",1);
+	  psMetadataAddS32(Fcell2->concepts,PS_LIST_TAIL,"CELL.READDIR", PS_META_REPLACE,"",1);
+
+	  psMetadataAddS32(Fcell1->parent->concepts,PS_LIST_TAIL,"CHIP.XPARITY", PS_META_REPLACE,"",1);
+	  psMetadataAddS32(Fcell1->parent->concepts,PS_LIST_TAIL,"CHIP.YPARITY", PS_META_REPLACE,"",1);
+	  psMetadataAddS32(Fcell2->parent->concepts,PS_LIST_TAIL,"CHIP.XPARITY", PS_META_REPLACE,"",1);
+	  psMetadataAddS32(Fcell2->parent->concepts,PS_LIST_TAIL,"CHIP.YPARITY", PS_META_REPLACE,"",1);
+
+	  pmReadout *Fro1 = pmReadoutAlloc(Fcell1), *Fro2 = pmReadoutAlloc(Fcell2); // Binned readouts
+	  
+	  Fro1->image = image1;
+	  Fro2->image = image2;
+	  Fro1->mask = mask1;
+	  Fro2->mask = mask2;
+	  
+	  Fro1->data_exists = Fcell1->data_exists = Fcell1->parent->data_exists = true;
+	  Fro2->data_exists = Fcell2->data_exists = Fcell2->parent->data_exists = true;
+	  
+	  pmFPAfile *fits1 = pmFPAfileSelectSingle(data->config->files, "PPSKYCELL.BIN1", 0);
+	  fits1->save = true;
+	  fits1->fileIndex = i;
+	  pmFPAfile *fits2 = pmFPAfileSelectSingle(data->config->files, "PPSKYCELL.BIN2", 0);
+	  fits2->save = true;
+	  fits2->fileIndex = i;
+	}
+
+        psFree(view);
 #if 0
         {
Index: /branches/pap/ppStack/src/Makefile.am
===================================================================
--- /branches/pap/ppStack/src/Makefile.am	(revision 28483)
+++ /branches/pap/ppStack/src/Makefile.am	(revision 28484)
@@ -48,4 +48,5 @@
 	ppStackPhotometry.c	\
 	ppStackFinish.c		\
+	ppStackTarget.c		\
 	ppStackErrorCodes.c
 
Index: /branches/pap/ppStack/src/ppStack.h
===================================================================
--- /branches/pap/ppStack/src/ppStack.h	(revision 28483)
+++ /branches/pap/ppStack/src/ppStack.h	(revision 28484)
@@ -26,4 +26,5 @@
 typedef enum {
     PPSTACK_FILES_PREPARE,              // Files for preparation
+    PPSTACK_FILES_TARGET,               // Files for target generation
     PPSTACK_FILES_CONVOLVE,             // Files for convolution
     PPSTACK_FILES_STACK,                // Stack files
@@ -115,6 +116,12 @@
 void ppStackVersionPrint(void);
 
+/// Generate target PSF image
+psImage *ppStackTarget(ppStackOptions *options, // Options for stacking
+                       pmConfig *config         // Configuration
+    );
+
 /// Convolve image to match specified seeing
 bool ppStackMatch(pmReadout *readout,   // Readout to be convolved; replaced with output
+                  const psImage *target,   // Target PSF image
                   ppStackOptions *options, // Options for stacking
                   int index,            // Index of image to match
Index: /branches/pap/ppStack/src/ppStackCombineFinal.c
===================================================================
--- /branches/pap/ppStack/src/ppStackCombineFinal.c	(revision 28483)
+++ /branches/pap/ppStack/src/ppStackCombineFinal.c	(revision 28484)
@@ -79,9 +79,7 @@
         PS_ARRAY_ADD_SCALAR(job->args, normalise, PS_TYPE_U8);
         if (!psThreadJobAddPending(job)) {
-            psFree(job);
             psFree(reject);
             return false;
         }
-        psFree(job);
     }
 
Index: /branches/pap/ppStack/src/ppStackCombineInitial.c
===================================================================
--- /branches/pap/ppStack/src/ppStackCombineInitial.c	(revision 28483)
+++ /branches/pap/ppStack/src/ppStackCombineInitial.c	(revision 28484)
@@ -50,8 +50,6 @@
         psArrayAdd(job->args, 1, config);
         if (!psThreadJobAddPending(job)) {
-            psFree(job);
             return false;
         }
-        psFree(job);
     }
 
Index: /branches/pap/ppStack/src/ppStackConvolve.c
===================================================================
--- /branches/pap/ppStack/src/ppStackConvolve.c	(revision 28483)
+++ /branches/pap/ppStack/src/ppStackConvolve.c	(revision 28484)
@@ -45,4 +45,13 @@
     options->convCovars = psArrayAlloc(num); // Covariance matrices
 
+    psImage *target = NULL;             // Target PSF image
+    if (options->convolve) {
+        target = ppStackTarget(options, config);
+        if (!target) {
+            psError(psErrorCodeLast(), false, "Unable to produce stack target image");
+            return false;
+        }
+    }
+
     psVector *renorms = psVectorAlloc(num, PS_TYPE_F32); // Renormalisation values for variances
     psVectorInit(renorms, NAN);
@@ -71,4 +80,5 @@
             psFree(fpaList);
             psFree(cellList);
+            psFree(target);
             return false;
         }
@@ -87,4 +97,5 @@
             psFree(fpaList);
             psFree(cellList);
+            psFree(target);
             return false;
         }
@@ -93,5 +104,5 @@
         psTimerStart("PPSTACK_MATCH");
         options->origCovars->data[i] = psMemIncrRefCounter(readout->covariance);
-        if (!ppStackMatch(readout, options, i, config)) {
+        if (!ppStackMatch(readout, target, options, i, config)) {
             // XXX many things can cause a failure of ppStackMatch -- should some be handled differently?
             psErrorCode error = psErrorCodeLast(); // Error code
@@ -102,4 +113,8 @@
               case PPSTACK_ERR_IO:
                 psError(error, false, "Unable to match image %d due to fatal error.", i);
+                psFree(rng);
+                psFree(fpaList);
+                psFree(cellList);
+                psFree(target);
                 return false;
                 // Non-fatal errors
@@ -154,4 +169,5 @@
             psFree(cellList);
             psFree(rng);
+            psFree(target);
             return false;
         }
@@ -164,4 +180,5 @@
             psFree(rng);
             psFree(maskHeader);
+            psFree(target);
             return false;
         }
@@ -172,4 +189,5 @@
             psFree(cellList);
             psFree(rng);
+            psFree(target);
             return false;
         }
@@ -222,4 +240,5 @@
             psFree(cellList);
             psFree(rng);
+            psFree(target);
             return false;
         }
@@ -229,4 +248,5 @@
     }
     psFree(rng);
+    psFree(target);
 
     psFree(options->sourceLists); options->sourceLists = NULL;
@@ -235,8 +255,11 @@
 
     if (numGood == 0) {
-        psError(PPSTACK_ERR_REJECTED, false, "No good images to combine.");
+        options->quality = PPSTACK_ERR_REJECTED;
+        psErrorStackPrint(stderr, "No good images survived convolution stage.");
+        psErrorClear();
+        psWarning("No good images survived convolution stage.");
         psFree(fpaList);
         psFree(cellList);
-        return false;
+        return true;
     }
 
@@ -324,6 +347,9 @@
 
     if (numGood == 0) {
-        psError(PPSTACK_ERR_REJECTED, false, "No good images to combine.");
-        return false;
+        options->quality = PPSTACK_ERR_REJECTED;
+        psErrorStackPrint(stderr, "No good images survived convolution stage.");
+        psErrorClear();
+        psWarning("No good images survived convolution stage.");
+        return true;
     }
 
Index: /branches/pap/ppStack/src/ppStackErrorCodes.h.in
===================================================================
--- /branches/pap/ppStack/src/ppStackErrorCodes.h.in	(revision 28483)
+++ /branches/pap/ppStack/src/ppStackErrorCodes.h.in	(revision 28484)
@@ -21,5 +21,5 @@
  */
 typedef enum {
-    PPSTACK_ERR_BASE = 14000,
+    PPSTACK_ERR_BASE = 13000,
     PPSTACK_ERR_${ErrorCode},
     PPSTACK_ERR_NERROR
Index: /branches/pap/ppStack/src/ppStackFiles.c
===================================================================
--- /branches/pap/ppStack/src/ppStackFiles.c	(revision 28483)
+++ /branches/pap/ppStack/src/ppStackFiles.c	(revision 28484)
@@ -16,8 +16,9 @@
 static char *filesPrepare[] = { "PPSTACK.INPUT.PSF", "PPSTACK.INPUT.SOURCES", "PPSTACK.TARGET.PSF", NULL };
 
+/// Files required for generating convolution target
+static char *filesTarget[] = { "PPSTACK.INPUT.VARIANCE", "PPSTACK.INPUT.MASK", NULL };
+
 /// Files required for the convolution
 static char *filesConvolve[] = { "PPSTACK.INPUT", "PPSTACK.INPUT.MASK", "PPSTACK.INPUT.VARIANCE", NULL };
-
-//                                 "PPSTACK.CONV.KERNEL", NULL };
 
 /// Regular (convolved) stack files
@@ -41,4 +42,5 @@
     switch (list) {
       case PPSTACK_FILES_PREPARE:  return filesPrepare;
+      case PPSTACK_FILES_TARGET:   return filesTarget;
       case PPSTACK_FILES_CONVOLVE: return filesConvolve;
       case PPSTACK_FILES_STACK:    return filesStack;
Index: /branches/pap/ppStack/src/ppStackLoop.c
===================================================================
--- /branches/pap/ppStack/src/ppStackLoop.c	(revision 28483)
+++ /branches/pap/ppStack/src/ppStackLoop.c	(revision 28484)
@@ -91,4 +91,9 @@
     ppStackMemDump("convolve");
 
+    if (options->quality) {
+        // Can't do anything else
+        return true;
+    }
+
     // Ensure sufficient inputs
     {
@@ -97,6 +102,9 @@
         bool safe = psMetadataLookupBool(NULL, recipe, "SAFE"); // Be safe when combining
         if (safe && numGood <= 1) {
-            psError(PPSTACK_ERR_REJECTED, true, "Insufficient inputs for combination with safety on");
-            return false;
+            options->quality = PPSTACK_ERR_REJECTED;
+            psErrorStackPrint(stderr, "Insufficient inputs for combination with safety on");
+            psErrorClear();
+            psWarning("Insufficient inputs for combination with safety on");
+            return true;
         }
     }
@@ -147,6 +155,9 @@
         int numGood = stackSummary(options, "final combination");
         if (numGood <= 0) {
-            psError(PPSTACK_ERR_REJECTED, true, "Insufficient inputs for combination");
-            return false;
+            options->quality = PPSTACK_ERR_REJECTED;
+            psErrorStackPrint(stderr, "Insufficient inputs survived rejection stage");
+            psErrorClear();
+            psWarning("Insufficient inputs survived rejection stage");
+            return true;
         }
     }
Index: /branches/pap/ppStack/src/ppStackMatch.c
===================================================================
--- /branches/pap/ppStack/src/ppStackMatch.c	(revision 28483)
+++ /branches/pap/ppStack/src/ppStackMatch.c	(revision 28484)
@@ -13,7 +13,4 @@
 #define MAG_IGNORE 50                   // Ignore magnitudes fainter than this --- they're not real!
 #define FAKE_SIZE 1                     // Size of fake convolution kernel
-#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
-#define NOISE_FRACTION 0.01             // Set minimum flux to this fraction of noise
 #define COVAR_FRAC 0.01                 // Truncation fraction for covariance matrix
 
@@ -49,77 +46,4 @@
 #endif
 
-// Get coordinates from a source
-static void coordsFromSource(float *x, float *y, const pmSource *source)
-{
-    assert(x && y);
-    assert(source);
-
-    if (source->modelPSF) {
-        *x = source->modelPSF->params->data.F32[PM_PAR_XPOS];
-        *y = source->modelPSF->params->data.F32[PM_PAR_YPOS];
-    } else {
-        *x = source->peak->xf;
-        *y = source->peak->yf;
-    }
-    return;
-}
-
-static psArray *stackSourcesFilter(psArray *sources, // Source list to filter
-                                   int exclusion // Exclusion zone, pixels
-    )
-{
-    psAssert(sources && sources->n > 0, "Require array of sources");
-    if (exclusion <= 0) {
-        return psMemIncrRefCounter(sources);
-    }
-
-    int num = sources->n;               // Number of sources
-    psVector *x = psVectorAlloc(num, PS_TYPE_F32), *y = psVectorAlloc(num, PS_TYPE_F32); // Coordinates
-    int numGood = 0;                    // Number of good sources
-    for (int i = 0; i < num; i++) {
-        pmSource *source = sources->data[i]; // Source of interest
-        if (!source) {
-            continue;
-        }
-        coordsFromSource(&x->data.F32[numGood], &y->data.F32[numGood], source);
-        numGood++;
-    }
-    x->n = y->n = numGood;
-
-    psTree *tree = psTreePlant(2, 2, PS_TREE_EUCLIDEAN, x, y); // kd tree
-
-    psArray *filtered = psArrayAllocEmpty(numGood); // Filtered list of sources
-    psVector *coords = psVectorAlloc(2, PS_TYPE_F64); // Coordinates of source
-    int numFiltered = 0;                // Number of filtered sources
-    for (int i = 0; i < num; i++) {
-        pmSource *source = sources->data[i]; // Source of interest
-        if (!source) {
-            continue;
-        }
-        float xSource, ySource;         // Coordinates of source
-        coordsFromSource(&xSource, &ySource, source);
-
-        coords->data.F64[0] = xSource;
-        coords->data.F64[1] = ySource;
-
-        long numWithin = psTreeWithin(tree, coords, exclusion); // Number within exclusion zone
-        psTrace("ppStack", 9, "Source at %.0lf,%.0lf has %ld sources in exclusion zone",
-                coords->data.F64[0], coords->data.F64[1], numWithin);
-        if (numWithin == 1) {
-            // Only itself inside the exclusion zone
-            filtered = psArrayAdd(filtered, filtered->n, source);
-        } else {
-            numFiltered++;
-        }
-    }
-    psFree(coords);
-    psFree(tree);
-    psFree(x);
-    psFree(y);
-
-    psLogMsg("ppStack", PS_LOG_INFO, "Filtered out %d of %d sources", numFiltered, numGood);
-
-    return filtered;
-}
 
 // Add background into the fake image
@@ -202,5 +126,6 @@
 
 
-bool ppStackMatch(pmReadout *readout, ppStackOptions *options, int index, const pmConfig *config)
+bool ppStackMatch(pmReadout *readout, const psImage *target, ppStackOptions *options,
+                  int index, const pmConfig *config)
 {
     assert(readout);
@@ -286,8 +211,6 @@
         } else {
 #endif
-
             // Normal operations here
-            psAssert(options->psf, "Require target PSF");
-            psAssert(options->sourceLists && options->sourceLists->data[index], "Require source list");
+            psAssert(target, "Require target PSF image");
 
             int order = psMetadataLookupS32(NULL, ppsub, "SPATIAL.ORDER"); // Spatial polynomial order
@@ -341,37 +264,6 @@
             }
 
-            pmReadout *fake = pmReadoutAlloc(NULL); // Fake readout with target PSF
-
-            psStats *bg = psStatsAlloc(PS_STAT_ROBUST_STDEV); // Statistics for background
-            psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS); // Random number generator
-            if (!psImageBackground(bg, NULL, readout->image, readout->mask, maskVal | maskBad, rng)) {
-                psError(PPSTACK_ERR_DATA, false, "Can't measure background for image.");
-                psFree(fake);
-                psFree(optWidths);
-                psFree(conv);
-                psFree(bg);
-                psFree(rng);
-                return false;
-            }
-            float minFlux = NOISE_FRACTION * bg->robustStdev; // Minimum flux level for fake image
-            psFree(rng);
-            psFree(bg);
-
-            // For the sake of stamps, remove nearby sources
-            psArray *stampSources = stackSourcesFilter(options->sourceLists->data[index],
-                                                       footprint); // Filtered list of sources
-
-            bool oldThreads = pmReadoutFakeThreads(true); // Old threading state
-            if (!pmReadoutFakeFromSources(fake, readout->image->numCols, readout->image->numRows,
-                                          stampSources, SOURCE_MASK, NULL, NULL, options->psf,
-                                          minFlux, footprint + size, false, true)) {
-                psError(PPSTACK_ERR_DATA, false, "Unable to generate fake image with target PSF.");
-                psFree(fake);
-                psFree(optWidths);
-                psFree(conv);
-                return false;
-            }
-            pmReadoutFakeThreads(oldThreads);
-
+            pmReadout *fake = pmReadoutAlloc(NULL);
+            fake->image = psImageCopy(NULL, target, PS_TYPE_F32);
             fake->mask = psImageCopy(NULL, readout->mask, PS_TYPE_IMAGE_MASK);
 
@@ -420,5 +312,4 @@
                     psFree(fake);
                     psFree(optWidths);
-                    psFree(stampSources);
                     psFree(conv);
                     if (threads > 0) {
@@ -436,5 +327,4 @@
                     psFree(fake);
                     psFree(optWidths);
-                    psFree(stampSources);
                     psFree(conv);
                     psFree(widthsCopy);
@@ -446,5 +336,5 @@
 
                 if (!pmSubtractionMatch(NULL, conv, fake, readout, footprint, stride, regionSize, spacing,
-                                        threshold, stampSources, stampsName, type, size, order, widthsCopy,
+                                        threshold, options->sources, stampsName, type, size, order, widthsCopy,
                                         orders, inner, ringsOrder, binning, penalty,
                                         optimum, optWidths, optOrder, optThresh, iter, rej, normFrac,
@@ -454,5 +344,4 @@
                     psFree(fake);
                     psFree(optWidths);
-                    psFree(stampSources);
                     psFree(conv);
                     psFree(widthsCopy);
@@ -492,5 +381,4 @@
             psFree(fake);
             psFree(optWidths);
-            psFree(stampSources);
 
             if (threads > 0) {
Index: /branches/pap/ppStack/src/ppStackReject.c
===================================================================
--- /branches/pap/ppStack/src/ppStackReject.c	(revision 28483)
+++ /branches/pap/ppStack/src/ppStackReject.c	(revision 28484)
@@ -54,8 +54,6 @@
         PS_ARRAY_ADD_SCALAR(job->args, i, PS_TYPE_S32);
         if (!psThreadJobAddPending(job)) {
-            psFree(job);
             return false;
         }
-        psFree(job);
     }
     if (!psThreadPoolWait(true)) {
@@ -166,9 +164,4 @@
     psFree(options->inspect); options->inspect = NULL;
 
-    if (numRejected >= num) {
-        psError(PPSTACK_ERR_REJECTED, true, "All inputs completely rejected; unable to proceed.");
-        return false;
-    }
-
     if (options->stats) {
         psMetadataAddS32(options->stats, PS_LIST_TAIL, "REJECT_IMAGES", 0,
Index: /branches/pap/ppStack/src/ppStackTarget.c
===================================================================
--- /branches/pap/ppStack/src/ppStackTarget.c	(revision 28484)
+++ /branches/pap/ppStack/src/ppStackTarget.c	(revision 28484)
@@ -0,0 +1,238 @@
+#include <stdio.h>
+#include <pslib.h>
+#include <psmodules.h>
+
+#include "ppStack.h"
+
+#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
+
+// Get coordinates from a source
+static void coordsFromSource(float *x, float *y, float *mag, const pmSource *source)
+{
+    assert(x && y);
+    assert(source);
+
+    if (source->modelPSF) {
+        *x = source->modelPSF->params->data.F32[PM_PAR_XPOS];
+        *y = source->modelPSF->params->data.F32[PM_PAR_YPOS];
+    } else {
+        *x = source->peak->xf;
+        *y = source->peak->yf;
+    }
+    if (mag) {
+        *mag = source->psfMag;
+    }
+    return;
+}
+
+// Filter a list of sources to exclude sources with near neighbours
+static psArray *stackSourcesFilter(psArray *sources, // Source list to filter
+                                   int exclusion, // Exclusion zone, pixels
+                                   float minMagDiff // Minimum magnitude difference
+    )
+{
+    psAssert(sources && sources->n > 0, "Require array of sources");
+    if (exclusion <= 0) {
+        return psMemIncrRefCounter(sources);
+    }
+
+    int num = sources->n;               // Number of sources
+    psVector *x = psVectorAlloc(num, PS_TYPE_F32), *y = psVectorAlloc(num, PS_TYPE_F32); // Coordinates
+    psVector *mag = psVectorAlloc(num, PS_TYPE_F32);                                     // Magnitudes
+    int numGood = 0;                    // Number of good sources
+    for (int i = 0; i < num; i++) {
+        pmSource *source = sources->data[i]; // Source of interest
+        if (!source) {
+            continue;
+        }
+        coordsFromSource(&x->data.F32[numGood], &y->data.F32[numGood], &mag->data.F32[numGood], source);
+        numGood++;
+    }
+    x->n = y->n = mag->n = numGood;
+
+    psTree *tree = psTreePlant(2, 2, PS_TREE_EUCLIDEAN, x, y); // kd tree
+
+    psArray *filtered = psArrayAllocEmpty(numGood); // Filtered list of sources
+    psVector *coords = psVectorAlloc(2, PS_TYPE_F64); // Coordinates of source
+    int numFiltered = 0;                // Number of filtered sources
+    for (int i = 0; i < num; i++) {
+        pmSource *source = sources->data[i]; // Source of interest
+        if (!source) {
+            continue;
+        }
+        float xSource, ySource;         // Coordinates of source
+        coordsFromSource(&xSource, &ySource, NULL, source);
+
+        coords->data.F64[0] = xSource;
+        coords->data.F64[1] = ySource;
+
+        psVector *indices = psTreeAllWithin(tree, coords, exclusion); // Number within exclusion zone
+        psTrace("ppStack", 9, "Source at %.0lf,%.0lf has %ld sources in exclusion zone",
+                coords->data.F64[0], coords->data.F64[1], indices->n);
+        if (indices->n == 1) {
+            // Only itself inside the exclusion zone
+            filtered = psArrayAdd(filtered, filtered->n, source);
+        } else {
+            float inMag = mag->data.F32[i]; // Input magnitude
+            bool filter = false;        // Filter this source?
+            for (int j = 0; j < indices->n; j++) {
+                long index = indices->data.S64[j]; // Index of matching source
+                float compareMag = mag->data.F32[index]; // Magnitude of matching source
+                if (fabsf(inMag - compareMag) < minMagDiff) {
+                    filter = true;
+                    break;
+                }
+            }
+            if (!filter) {
+                filtered = psArrayAdd(filtered, filtered->n, source);
+            } else {
+                numFiltered++;
+            }
+        }
+    }
+    psFree(coords);
+    psFree(tree);
+    psFree(x);
+    psFree(y);
+    psFree(mag);
+
+    psLogMsg("ppStack", PS_LOG_INFO, "Filtered out %d of %d sources", numFiltered, numGood);
+
+    return filtered;
+}
+
+
+psImage *ppStackTarget(ppStackOptions *options, pmConfig *config)
+{
+    psAssert(options, "Require options");
+    psAssert(config, "Require configuration");
+
+    psAssert(options->psf, "Require target PSF");
+    psAssert(options->sourceLists, "Require source lists");
+
+    psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, PPSTACK_RECIPE); // ppStack recipe
+    psAssert(recipe, "We've thrown an error on this before.");
+    psMetadata *ppsub = psMetadataLookupMetadata(NULL, config->recipes, "PPSUB"); // PPSUB recipe
+    psAssert(recipe, "We've thrown an error on this before.");
+
+    psString maskValStr = psMetadataLookupStr(NULL, recipe, "MASK.VAL"); // Name of input bits to mask
+    psImageMaskType maskVal = pmConfigMaskGet(maskValStr, config); // Input bits to mask
+    float targetFrac = psMetadataLookupF32(NULL, recipe, "TARGET.FRAC"); // Target min flux fraction of noise
+
+    int num = options->num;             // Number of inputs
+    int numCols = 0, numRows = 0;       // Size of image
+
+    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS); // Random number generator
+    float minVariance = INFINITY;       // Minimum variance
+    for (int i = 0; i < num; i++) {
+        if (options->inputMask->data.U8[i]) {
+            continue;
+        }
+        psTrace("ppStack", 2, "Characterising image %d....\n", i);
+        pmFPAfileActivate(config->files, false, NULL);
+        ppStackFileActivationSingle(config, PPSTACK_FILES_TARGET, true, i);
+
+        pmFPAfile *file = pmFPAfileSelectSingle(config->files, "PPSTACK.INPUT.VARIANCE", i); // File to read
+        pmFPAview *view = ppStackFilesIterateDown(config);
+        if (!view) {
+            psFree(rng);
+            return NULL;
+        }
+        pmReadout *readout = pmFPAviewThisReadout(view, file->fpa); // Input readout
+        psFree(view);
+
+        if (numCols == 0 && numRows == 0) {
+            numCols = readout->variance->numCols;
+            numRows = readout->variance->numRows;
+        } else if (numCols != readout->variance->numCols ||
+                   numRows != readout->variance->numRows) {
+            psError(PPSTACK_ERR_ARGUMENTS, true, "Sizes of input images don't match: %dx%d vs %dx%d",
+                    readout->variance->numCols, readout->variance->numRows, numCols, numRows);
+            psFree(rng);
+            return NULL;
+        }
+
+        psImage *variance = readout->variance, *mask = readout->mask; // Dereference images
+        for (int y = 0; y < numRows; y++) {
+            for (int x = 0; x < numCols; x++) {
+                if (!isfinite(variance->data.F32[y][x])) {
+                    mask->data.PS_TYPE_IMAGE_MASK_DATA[y][x] |= maskVal;
+                }
+            }
+        }
+
+        psStats *bg = psStatsAlloc(PS_STAT_ROBUST_MEDIAN); // Statistics for background
+        float mean = NAN;                                  // Measured mean variance
+        if (!psImageBackground(bg, NULL, variance, mask, maskVal, rng)) {
+            psErrorClear();
+            // Retry using all the available pixels
+            bg->nSubsample = variance->numCols * variance->numRows + 1;
+            if (!psImageStats(bg, variance, mask, maskVal)) {
+                psLogMsg("ppStack", PS_LOG_DETAIL,
+                         "Couldn't measure mean variance for image %d; retrying.", i);
+                psErrorClear();
+                // Retry with desperate statistic
+                bg->options = PS_STAT_SAMPLE_MEAN;
+                if (!psImageStats(bg, variance, mask, maskVal)) {
+                    psWarning("Unable to measure mean variance for image %d --- rejecting.", i);
+                    psErrorStackPrint(stderr, "Unable to measure mean variance for image %d", i);
+                    options->inputMask->data.PS_TYPE_VECTOR_MASK_DATA[i] |= PPSTACK_MASK_PSF;
+                    goto DONE;
+                } else {
+                    // Desperate retry
+                    mean = bg->sampleMean;
+                }
+            } else {
+                // Retry with all available pixels
+                mean = bg->robustMedian;
+            }
+        } else {
+            // First attempt
+            mean = bg->robustMedian;
+        }
+
+        float norm = powf(10.0, -0.4 * options->norm->data.F32[i]); // Normalisation from stars
+        float meanVariance = bg->robustMedian * PS_SQR(norm);       // Mean variance in normalised image
+
+        if (meanVariance < minVariance) {
+            minVariance = meanVariance;
+        }
+
+    DONE:
+        psFree(bg);
+        if (!ppStackFilesIterateUp(config)) {
+            psFree(rng);
+            return NULL;
+        }
+
+        ppStackMemDump("target");
+    }
+    psFree(rng);
+
+    float minFlux = targetFrac * sqrtf(minVariance); // Minimum flux for target image
+
+    int footprint = psMetadataLookupS32(NULL, ppsub, "STAMP.FOOTPRINT"); // Stamp half-size
+    int size = psMetadataLookupS32(NULL, ppsub, "KERNEL.SIZE"); // Kernel half-size
+    float minMagDiff = psMetadataLookupF32(NULL, recipe, "TARGET.MINMAG"); // Minimum magnitude difference
+
+    // For the sake of stamps, remove nearby sources
+    psArray *stampSources = stackSourcesFilter(options->sources, footprint, minMagDiff); // Filtered list
+
+    bool oldThreads = pmReadoutFakeThreads(true); // Old threading state
+    pmReadout *fake = pmReadoutAlloc(NULL); // Fake readout with target PSF
+    if (!pmReadoutFakeFromSources(fake, numCols, numRows, stampSources, SOURCE_MASK, NULL, NULL, options->psf,
+                                  minFlux, footprint + size, false, true)) {
+        psError(PPSTACK_ERR_DATA, false, "Unable to generate fake image with target PSF.");
+        psFree(fake);
+        return NULL;
+    }
+    pmReadoutFakeThreads(oldThreads);
+
+    psFree(stampSources);
+
+    psImage *target = psMemIncrRefCounter(fake->image);
+    psFree(fake);
+
+    return target;
+}
Index: /branches/pap/ppSub/src/ppSubDefineOutput.c
===================================================================
--- /branches/pap/ppSub/src/ppSubDefineOutput.c	(revision 28483)
+++ /branches/pap/ppSub/src/ppSubDefineOutput.c	(revision 28484)
@@ -16,4 +16,5 @@
 
 #include <stdio.h>
+#include <libgen.h>
 #include <pslib.h>
 #include <psmodules.h>
@@ -71,10 +72,19 @@
     pmFPAfile *refFile = psMetadataLookupPtr(NULL, config->files, "PPSUB.REF"); // Reference file
     pmFPAfile *inFile = psMetadataLookupPtr(NULL, config->files, "PPSUB.INPUT"); // Input file
+
+    // We should be using the GNU version of basename() which doesn't modify its argument, but just in case,
+    // we'll copy the string.
+    psString refName = psStringCopy(refFile->filename); // Filename of reference
+    const char *refBase = basename(refName);            // Basename of reference
     psMetadataAddStr(outHDU->header, PS_LIST_TAIL, "PPSUB.REFERENCE", 0,
-                     "Subtraction reference", refFile->filename);
+                     "Subtraction reference", refBase);
+    psFree(refName);
+    psString inName = psStringCopy(inFile->filename);   // Filename of input
+    const char *inBase = basename(inName);              // Basename of input
     psMetadataAddStr(outHDU->header, PS_LIST_TAIL, "PPSUB.INPUT", 0,
-                     "Subtraction input", inFile->filename);
+                     "Subtraction input", inBase);
+    psFree(inName);
+
     ppSubVersionHeader(outHDU->header);
-
 
     outRO->analysis = psMetadataCopy(outRO->analysis, analysis);
Index: /branches/pap/ppTranslate/src/ppMops.c
===================================================================
--- /branches/pap/ppTranslate/src/ppMops.c	(revision 28483)
+++ /branches/pap/ppTranslate/src/ppMops.c	(revision 28484)
@@ -20,17 +20,15 @@
     }
 
-    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);
+    psFree(detections);
     psFree(args);
 
Index: /branches/pap/ppTranslate/src/ppMops.h
===================================================================
--- /branches/pap/ppTranslate/src/ppMops.h	(revision 28483)
+++ /branches/pap/ppTranslate/src/ppMops.h	(revision 28484)
@@ -7,7 +7,4 @@
 #define IN_EXTNAME "SkyChip.psf"        // Extension name for data in input
 #define OBSERVATORY_CODE "F51"          // IAU Observatory Code
-#define OUT_EXTNAME "MOPS_TRANSIENT_DETECTIONS" // Extension name for data in output
-#define SOURCE_MASK (PM_SOURCE_MODE_FAIL | PM_SOURCE_MODE_BADPSF | PM_SOURCE_MODE_SATURATED | \
-                     PM_SOURCE_MODE_CR_LIMIT | PM_SOURCE_MODE_SKY_FAILURE) // Flags to exclude
 
 // Configuration data
@@ -27,35 +24,10 @@
 } ppMopsArguments;
 
-#if 0
-TTYPE19 = 'PSF_CHISQ'          / label for field  19
-TFORM19 = '1E      '           / data format of field: 4-byte REAL
-TTYPE20 = 'CR_NSIGMA'          / label for field  20
-TFORM20 = '1E      '           / data format of field: 4-byte REAL
-TTYPE21 = 'EXT_NSIGMA'         / label for field  21
-TFORM21 = '1E      '           / data format of field: 4-byte REAL
-TTYPE22 = 'PSF_MAJOR'          / label for field  22
-TFORM22 = '1E      '           / data format of field: 4-byte REAL
-TTYPE23 = 'PSF_MINOR'          / label for field  23
-TFORM23 = '1E      '           / data format of field: 4-byte REAL
-TTYPE24 = 'PSF_THETA'          / label for field  24
-TFORM24 = '1E      '           / data format of field: 4-byte REAL
-TTYPE25 = 'PSF_QF  '           / label for field  25
-TFORM25 = '1E      '           / data format of field: 4-byte REAL
-TTYPE26 = 'PSF_NDOF'           / label for field  26
-TFORM26 = '1J      '           / data format of field: 4-byte INTEGER
-TTYPE27 = 'PSF_NPIX'           / label for field  27
-TFORM27 = '1J      '           / data format of field: 4-byte INTEGER
-TTYPE28 = 'MOMENTS_XX'         / label for field  28
-TFORM28 = '1E      '           / data format of field: 4-byte REAL
-TTYPE29 = 'MOMENTS_XY'         / label for field  29
-TFORM29 = '1E      '           / data format of field: 4-byte REAL
-TTYPE30 = 'MOMENTS_YY'         / label for field  30
-TFORM30 = '1E      '           / data format of field: 4-byte REAL
-#endif
-
 /// Parse arguments
 ppMopsArguments *ppMopsArgumentsParse(int argc, char *argv[]);
 
 typedef struct {
+    psString component;                 // Component name
+    psMetadata *header;                 // FITS header (extension names *.hdr)
     psString raBoresight, decBoresight; // RA,Dec of telescope boresight
     psString filter;                    // Filter for exposure
@@ -64,45 +36,27 @@
     double posangle;                    // Position angle
     double alt, az;                     // Telescope altitude and azimuth
-    double mjd;                         // Modified Julian Date
+    double mjd;                         // Modified Julian Date of exposure mid-point
     float seeing;                       // Seeing of exposure
+    int naxis1, naxis2;                 // Size of image
     long num;                           // Number of detections
+    psMetadata *psfHeader;              // FITS header (extension names *.psf)
+    psMetadata *table;                  // Columns of data
     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
+    psMetadata *deteffHeader;           // Detection efficiency header (extension names *.deteff)
+    psMetadata *deteffTable;            // Detection efficiency table
 } ppMopsDetections;
 
-
-ppMopsDetections *ppMopsDetectionsAlloc(long num);
-
-/// Copy a detection
-bool ppMopsDetectionsCopySingle(ppMopsDetections *target, const ppMopsDetections *source, long index);
-
-/// Purge the detections list of masked detections
-bool ppMopsDetectionsPurge(ppMopsDetections *detections);
-
+// Allocator
+ppMopsDetections *ppMopsDetectionsAlloc(void);
 
 /// Read detections
 psArray *ppMopsRead(const ppMopsArguments *args);
 
-/// Merge detections
-ppMopsDetections *ppMopsMerge(const psArray *detections);
+/// Purge duplicate detections
+bool ppMopsPurgeDuplicates(const psArray *detections);
 
 /// Write detections
-bool ppMopsWrite(const ppMopsDetections *detections, const ppMopsArguments *args);
+bool ppMopsWrite(const psArray *detections, const ppMopsArguments *args);
 
 #endif
Index: /branches/pap/ppTranslate/src/ppMopsDetections.c
===================================================================
--- /branches/pap/ppTranslate/src/ppMopsDetections.c	(revision 28483)
+++ /branches/pap/ppTranslate/src/ppMopsDetections.c	(revision 28484)
@@ -10,46 +10,26 @@
 static void mopsDetectionsFree(ppMopsDetections *det)
 {
+    psFree(det->component);
     psFree(det->raBoresight);
     psFree(det->decBoresight);
     psFree(det->filter);
+    psFree(det->header);
+    psFree(det->table);
     psFree(det->x);
     psFree(det->y);
     psFree(det->ra);
     psFree(det->dec);
-    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->deteffHeader);
+    psFree(det->deteffTable);
 
     return;
 }
 
-ppMopsDetections *ppMopsDetectionsAlloc(long num)
+ppMopsDetections *ppMopsDetectionsAlloc(void)
 {
     ppMopsDetections *det = psAlloc(sizeof(ppMopsDetections)); // Detections, to return
     psMemSetDeallocator(det, (psFreeFunc)mopsDetectionsFree);
 
+    det->component = NULL;
     det->raBoresight = NULL;
     det->decBoresight = NULL;
@@ -63,234 +43,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->header = NULL;
+    det->table = NULL;
+    det->x = NULL;
+    det->y = NULL;
+    det->ra = NULL;
+    det->dec = NULL;
+    det->deteffHeader = NULL;
+    det->deteffTable = NULL;
 
     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);
-
-    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)
-{
-    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);
-
-    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]);
-
-    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];
-            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;
-
-    return true;
-}
-
Index: /branches/pap/ppTranslate/src/ppMopsMerge.c
===================================================================
--- /branches/pap/ppTranslate/src/ppMopsMerge.c	(revision 28483)
+++ /branches/pap/ppTranslate/src/ppMopsMerge.c	(revision 28484)
@@ -9,5 +9,5 @@
 #include "ppMops.h"
 
-#define LEAF_SIZE 4                     // Size of leaf
+#define LEAF_SIZE 2                     // Size of leaf
 #define MATCH_RADIUS SEC_TO_RAD(1.0)    // Matching radius
 #define MJD_TOL 1.0/3600.0/24.0         // Tolerance for MJD matching
@@ -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,92 +30,139 @@
     )
 {
-    float dx = detections->x->data.F32[index] - detections->naxis1->data.S32[index] / 2.0;
-    float dy = detections->y->data.F32[index] - detections->naxis2->data.S32[index] / 2.0;
+    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 = 1;                                                         // 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++) {
@@ -119,14 +174,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 +189,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,30 +207,84 @@
             }
 
-            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;
-                }
-                ppMopsDetectionsCopySingle(merged, det, j);
-            } else {
-                psTrace("ppMops.merge", 6, "Old sources clobber new source\n");
+            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]++;
+                }
             }
             psFree(indices);
         }
-
-        psTrace("ppMops.merge", 3, "Done merging input %d, %ld merged sources\n", i, merged->num);
-
-        psFree(tree);
-        ppMopsDetectionsPurge(merged);
-    }
-
-    psTrace("ppMops.merge", 2, "%ld sources in merged detections list\n", merged->num);
-
-    merged->seeing /= 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:                                         \
+          for (int i = 0, j = 0; i < vector->n; i++) {               \
+              if (!dupes->data.U8[i]) {                              \
+                  if (i == j) {                                      \
+                      j++;                                           \
+                      continue;                                      \
+                  }                                                  \
+                  vector->data.TYPE[j] = vector->data.TYPE[i];       \
+              }                                                      \
+          }                                                          \
+          break;
+
+        psVector *dupes = duplicates->data[i]; // Duplicates
+        psArray *table = psListToArray(det->table->list); // Table of data
+        for (int j = 0; j < table->n; j++) {
+            psMetadataItem *item = table->data[j]; // 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);
+            }
+        }
+    }
+
+    return true;
 }
 
Index: /branches/pap/ppTranslate/src/ppMopsRead.c
===================================================================
--- /branches/pap/ppTranslate/src/ppMopsRead.c	(revision 28483)
+++ /branches/pap/ppTranslate/src/ppMopsRead.c	(revision 28484)
@@ -4,4 +4,5 @@
 
 #include <stdio.h>
+#include <string.h>
 #include <pslib.h>
 
@@ -16,5 +17,6 @@
     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]; // File name
+        psFits *fits = psFitsOpen(name, "r"); // FITS file
         if (!fits) {
             psError(PS_ERR_IO, false, "Unable to open input %d", i);
@@ -24,10 +26,4 @@
         if (!header) {
             psError(PS_ERR_IO, false, "Unable to read header %d", i);
-            return false;
-        }
-
-        psS64 diffSkyfileId = psMetadataLookupS64(NULL, header, "IMAGEID"); // Identifier for image
-        if (diffSkyfileId == 0) {
-            psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to find identifier for image %d", i);
             return false;
         }
@@ -47,7 +43,8 @@
             continue;
         }
-        ppMopsDetections *det = ppMopsDetectionsAlloc(size);
-
-        psTrace("ppMops.read", 3, "Reading %ld rows from %s\n", size, (const char*)inNames->data[i]);
+        ppMopsDetections *det = detections->data[i] = ppMopsDetectionsAlloc();
+        det->component = psStringNCopy(name, strrchr(name, '.') - name); // Strip off extension
+        det->header = header;
+        det->num = size;
 
         det->raBoresight = psMemIncrRefCounter(psMetadataLookupStr(NULL, header, "FPA.RA"));
@@ -64,132 +61,55 @@
                              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");
+        det->naxis2 = psMetadataLookupS32(NULL, header, "IMNAXIS2");
 
-        psFree(header);
-
-        psArray *table = psFitsReadTable(fits); // Table of interest
-        if (!table) {
-            psError(PS_ERR_IO, false, "Unable to read table %d", i);
+        det->psfHeader = psFitsReadHeader(NULL, fits);
+        if (!det->psfHeader) {
+            psError(psErrorCodeLast(), false, "Unable to read header %d", i);
             return false;
         }
-        psFitsClose(fits);
-
-        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");
-            if (flags & SOURCE_MASK) {
-                psTrace("ppMops.read", 10, "Discarding row %ld from input %d because of flags: %ud", j, i, flags);
-                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");
-
-            // Calculate error in RA, Dec
-            double xErr = psMetadataLookupF64(NULL, row, "X_PSF_SIG");
-            double yErr = psMetadataLookupF64(NULL, row, "Y_PSF_SIG");
-            double scale = psMetadataLookupF64(NULL, row, "PLTSCALE");
-            double angle = psMetadataLookupF64(NULL, row, "POSANGLE");
-
-            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]) ||
-                !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],
-                        xErr, yErr, scale, angle);
-                continue;
-            }
-
-            // XXX Not at all sure I've got the angles around the right way here...
-            double cosAngle = cos(angle), sinAngle = sin(angle);
-            double cosAngle2 = PS_SQR(cosAngle), sinAngle2 = PS_SQR(sinAngle);
-            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;
-            plateScale += scale;
-            numGood++;
-        }
-        det->seeing *= plateScale / 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->num = numGood;
-
-        if (isfinite(args->zp) && numGood > 0) {
-            psBinaryOp(det->mag, det->mag, "+", psScalarAlloc(args->zp, PS_TYPE_F32));
+        psMetadata *table = det->table = psFitsReadTableAllColumns(fits); // Table of interest
+        if (!table) {
+            psError(psErrorCodeLast(), false, "Unable to read table %d", i);
+            return false;
         }
 
-        psTrace("ppMops.read", 2, "Read %ld good rows from %s\n", numGood, (const char*)inNames->data[i]);
+        psVector *ra = psMetadataLookupVector(NULL, table, "RA_PSF");
+        psVector *dec = psMetadataLookupVector(NULL, table, "DEC_PSF");
 
-        psFree(table);
-        detections->data[i] = det;
+        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 false;
+        }
+
+        psTrace("ppMops.read", 2, "Read %ld rows from %s\n", det->num, det->component);
+
+        if (!psFitsMoveExtName(fits, "SkyChip.deteff")) {
+            psWarning("No detection efficiencies included in %s", det->component);
+            psErrorStackPrint(stderr, "No detection efficiencies included in %s", det->component);
+            psErrorClear();
+            continue;
+        }
+
+        det->deteffHeader = psFitsReadHeader(NULL, fits);
+        if (!det->deteffHeader) {
+            psWarning("Unable to read detection efficiency header in %s", det->component);
+            psErrorClear();
+            continue;
+        }
+        det->deteffTable = psFitsReadTableAllColumns(fits);
+        if (!det->deteffTable) {
+            psFree(det->deteffHeader);
+            det->deteffHeader = NULL;
+            psWarning("Unable to read detection efficiency table in %s", det->component);
+            psErrorClear();
+            continue;
+        }
+        psTrace("ppMops.read", 2, "Read detection efficiency from %s\n", det->component);
+        psFitsClose(fits);
     }
 
Index: /branches/pap/ppTranslate/src/ppMopsWrite.c
===================================================================
--- /branches/pap/ppTranslate/src/ppMopsWrite.c	(revision 28483)
+++ /branches/pap/ppTranslate/src/ppMopsWrite.c	(revision 28484)
@@ -9,7 +9,7 @@
 #include "ppTranslateVersion.h"
 
-bool ppMopsWrite(const ppMopsDetections *det, const ppMopsArguments *args)
+bool ppMopsWrite(const psArray *detections, const ppMopsArguments *args)
 {
-    psTrace("ppMops.write", 1, "Writing %ld rows to %s", det->num, args->output);
+    psTrace("ppMops.write", 1, "Writing %ld extensions to %s", detections->n, args->output);
 
     psFits *fits = psFitsOpen(args->output, "w"); // FITS file
@@ -19,134 +19,67 @@
     }
 
+    // Primary header
+    {
+        psMetadata *header = psMetadataAlloc(); // Header to write
+        ppTranslateVersionHeader(header);
+        psMetadataAddStr(header, PS_LIST_TAIL, "EXP_NAME", 0, "Exposure name", args->exp_name);
+        psMetadataAddS64(header, PS_LIST_TAIL, "EXP_ID", 0, "Exposure identifier", args->exp_id);
+        psMetadataAddS64(header, PS_LIST_TAIL, "CHIP_ID", 0, "Chip stage identifier", args->chip_id);
+        psMetadataAddS64(header, PS_LIST_TAIL, "CAM_ID", 0, "Cam stage identifier", args->cam_id);
+        psMetadataAddS64(header, PS_LIST_TAIL, "FAKE_ID", 0, "Fake stage identifier", args->fake_id);
+        psMetadataAddS64(header, PS_LIST_TAIL, "WARP_ID", 0, "Warp stage identifier", args->warp_id);
+        psMetadataAddS64(header, PS_LIST_TAIL, "DIFF_ID", 0, "Diff stage identifier", args->diff_id);
+        psMetadataAddBool(header, PS_LIST_TAIL, "DIFF_POS", 0, "Positive subtraction?", args->positive);
+        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);
+        psMetadataAddStr(header, PS_LIST_TAIL, "OBSCODE", 0, "IAU Observatory code", OBSERVATORY_CODE);
 
-    psMetadata *header = psMetadataAlloc(); // Header to write
-    psString source = ppTranslateSource(), version = ppTranslateVersion();
-    psMetadataAddStr(header, PS_LIST_TAIL, "SWSOURCE", 0, "Software source", source);
-    psMetadataAddStr(header, PS_LIST_TAIL, "SWVERSN", 0, "Software version", version);
-    ppTranslateVersionHeader(header);
-    psFree(source);
-    psFree(version);
+        ppMopsDetections *det = detections->data[0]; // Representative set of detections
+        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);
+        psMetadataAddStr(header, PS_LIST_TAIL, "FILTER", 0, "Filter name", det->filter);
+        psMetadataAddF32(header, PS_LIST_TAIL, "AIRMASS", 0, "Airmass at boresight", det->airmass);
+        psMetadataAddF32(header, PS_LIST_TAIL, "EXPTIME", 0, "Exposure time (sec)", det->exptime);
+        psMetadataAddF64(header, PS_LIST_TAIL, "POSANG", 0, "Position angle", det->posangle);
+        psMetadataAddF64(header, PS_LIST_TAIL, "ALT", 0, "Altitude of boresight", det->alt);
+        psMetadataAddF64(header, PS_LIST_TAIL, "AZ", 0, "Azimuth of boresight", det->az);
+        psMetadataAddF64(header, PS_LIST_TAIL, "MJD-OBS", 0, "MJD of observation", det->mjd);
 
-    psMetadataAddStr(header, PS_LIST_TAIL, "EXP_NAME", 0, "Exposure name", args->exp_name);
-    psMetadataAddS64(header, PS_LIST_TAIL, "EXP_ID", 0, "Exposure identifier", args->exp_id);
-    psMetadataAddS64(header, PS_LIST_TAIL, "CHIP_ID", 0, "Chip stage identifier", args->chip_id);
-    psMetadataAddS64(header, PS_LIST_TAIL, "CAM_ID", 0, "Cam stage identifier", args->cam_id);
-    psMetadataAddS64(header, PS_LIST_TAIL, "FAKE_ID", 0, "Fake stage identifier", args->fake_id);
-    psMetadataAddS64(header, PS_LIST_TAIL, "WARP_ID", 0, "Warp stage identifier", args->warp_id);
-    psMetadataAddS64(header, PS_LIST_TAIL, "DIFF_ID", 0, "Diff stage identifier", args->diff_id);
-    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);
-    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);
-
-    if (det->num == 0) {
-        // Write dummy table
-        psMetadata *row = psMetadataAlloc(); // Output row
-        psMetadataAddF64(row, PS_LIST_TAIL, "RA", 0, "Right ascension (degrees)", NAN);
-        psMetadataAddF64(row, PS_LIST_TAIL, "RA_ERR", 0, "Right ascension error (degrees)", NAN);
-        psMetadataAddF64(row, PS_LIST_TAIL, "DEC", 0, "Declination (degrees)", NAN);
-        psMetadataAddF64(row, PS_LIST_TAIL, "DEC_ERR", 0, "Declination error (degrees)", NAN);
-        psMetadataAddF32(row, PS_LIST_TAIL, "MAG", 0, "Magnitude", NAN);
-        psMetadataAddF32(row, PS_LIST_TAIL, "MAG_ERR", 0, "Magnitude error", NAN);
-        psMetadataAddF32(row, PS_LIST_TAIL, "PSF_CHI2", 0, "chi^2 of PSF fit", NAN);
-        psMetadataAddS32(row, PS_LIST_TAIL, "PSF_DOF", 0, "Degrees of freedom of PSF fit", 0);
-        psMetadataAddF32(row, PS_LIST_TAIL, "CR_SIGNIFICANCE", 0, "Significance of CR", NAN);
-        psMetadataAddF32(row, PS_LIST_TAIL, "EXT_SIGNIFICANCE", 0, "Significance of extendedness", NAN);
-        psMetadataAddF32(row, PS_LIST_TAIL, "PSF_MAJOR", 0, "PSF major axis (pixels)", NAN);
-        psMetadataAddF32(row, PS_LIST_TAIL, "PSF_MINOR", 0, "PSF minor axis (pixels)", NAN);
-        psMetadataAddF32(row, PS_LIST_TAIL, "PSF_THETA", 0, "PSF position angle (deg on chip)", NAN);
-        psMetadataAddF32(row, PS_LIST_TAIL, "PSF_QUALITY", 0, "PSF quality factor", NAN);
-        psMetadataAddS32(row, PS_LIST_TAIL, "PSF_NPIX", 0, "Number of pixels in PSF", 0);
-        psMetadataAddF32(row, PS_LIST_TAIL, "MOMENTS_XX", 0, "xx moment", NAN);
-        psMetadataAddF32(row, PS_LIST_TAIL, "MOMENTS_XY", 0, "xy moment", NAN);
-        psMetadataAddF32(row, PS_LIST_TAIL, "MOMENTS_YY", 0, "yy moment", NAN);
-        psMetadataAddU32(row, PS_LIST_TAIL, "FLAGS", 0, "Detection bit flags", 0);
-        psMetadataAddS64(row, PS_LIST_TAIL, "DIFF_SKYFILE_ID", 0, "Identifier for diff skyfile", 0);
-
-        psMetadataAddS32(row, PS_LIST_TAIL, "N_POS", 0, "Number of positive pixels", 0);
-        psMetadataAddF32(row, PS_LIST_TAIL, "F_POS", 0, "Fraction of positive pixels", NAN);
-        psMetadataAddF32(row, PS_LIST_TAIL, "RATIO_BAD", 0, "Ratio of positive pixels to negative", NAN);
-        psMetadataAddF32(row, PS_LIST_TAIL, "RATIO_MASK", 0, "Ratio of positive pixels to masked", NAN);
-        psMetadataAddF32(row, PS_LIST_TAIL, "RATIO_ALL", 0, "Ratio of positive pixels to all", NAN);
-        if (!psFitsWriteTableEmpty(fits, header, row, OUT_EXTNAME)) {
-            psErrorStackPrint(stderr, "Unable to write empty table.");
-            psFree(header);
-            psFree(row);
+        if (!psFitsWriteBlank(fits, header, NULL)) {
+            psError(psErrorCodeLast(), false, "Unable to write primary header");
             return false;
         }
-        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]);
+        psFree(header);
+    }
 
-            table->data[i] = row;
-        }
-        if (!psFitsWriteTable(fits, header, table, OUT_EXTNAME)) {
-            psErrorStackPrint(stderr, "Unable to write table.");
-            psFree(header);
-            psFree(table);
+    for (int i = 0; i < detections->n; i++) {
+        ppMopsDetections *det = detections->data[i]; // Detections for extension
+        psTrace("ppMops.write", 1, "Writing extension %d to %s", i, args->output);
+        psString hdrName = NULL, psfName = NULL, deteffName = NULL;
+        psStringAppend(&hdrName, "%s.hdr", det->component);
+        psStringAppend(&psfName, "%s.psf", det->component);
+        psStringAppend(&deteffName, "%s.deteff", det->component);
+
+        if (!psFitsWriteBlank(fits, det->header, hdrName)) {
+            psError(psErrorCodeLast(), false, "Unable to write header %d", i);
             return false;
         }
-        psFree(table);
+        if (!psFitsWriteTableAllColumns(fits, det->psfHeader, det->table, psfName)) {
+            psError(psErrorCodeLast(), false, "Unable to write table %d", i);
+            return false;
+        }
+        if (det->deteffHeader && det->deteffTable &&
+            !psFitsWriteTableAllColumns(fits, det->deteffHeader, det->deteffTable, deteffName)) {
+            psError(psErrorCodeLast(), false, "Unable to write detection efficiency %d", i);
+            return false;
+        }
+        psFree(hdrName);
+        psFree(psfName);
+        psFree(deteffName);
     }
-
-    psFree(header);
     psFitsClose(fits);
 
-    psTrace("ppMops.write", 1, "Done writing %ld rows to %s", det->num, args->output);
+    psTrace("ppMops.write", 1, "Done writing to %s", args->output);
 
     return true;
Index: /branches/pap/psLib/src/db/psDB.c
===================================================================
--- /branches/pap/psLib/src/db/psDB.c	(revision 28483)
+++ /branches/pap/psLib/src/db/psDB.c	(revision 28484)
@@ -1524,9 +1524,24 @@
             }
         case PS_DATA_F64: {
-                bind[i].length  = 0;
-                bind[i].buffer  = &item->data.F64;
-                bind[i].is_null = psDBIsPTypeNaN(item->type, &item->data.F64) ? &isNull : NULL;
-                break;
-            }
+            // This hack is to work around a MySQL bug, where values of DBL_MAX are dumped (as text) with
+            // insufficient digits, causing the value to be rounded outside the bounds of DBL_MAX
+            // (specifically, -1.7976931348623157e+308 gets dumped as -1.79769313486232e+308 which is less
+            // than -DBL_MAX), which cannot then be loaded by MySQL.  We assume that we're using doubles for
+            // additional precision compared to floats, and not for additional size, so limiting to the
+            // maximum value of a float is not damaging.
+            if (item->data.F64 < -FLT_MAX) {
+                psWarning("Saturating double value at -FLT_MAX to work around MySQL bug: %lf --> %lf",
+                          item->data.F64, -FLT_MAX);
+                item->data.F64 = -FLT_MAX;
+            } else if (item->data.F64 > FLT_MAX) {
+                psWarning("Saturating double value at FLT_MAX to work around MySQL bug: %lf --> %lf",
+                          item->data.F64, FLT_MAX);
+                item->data.F64 = FLT_MAX;
+            }
+            bind[i].length  = 0;
+            bind[i].buffer  = &item->data.F64;
+            bind[i].is_null = psDBIsPTypeNaN(item->type, &item->data.F64) ? &isNull : NULL;
+            break;
+        }
         case PS_DATA_BOOL: {
                 // XXX: ASC HACK NOTE (2005/06/03): set extreme bytes to the
Index: /branches/pap/psLib/src/fits/psFits.c
===================================================================
--- /branches/pap/psLib/src/fits/psFits.c	(revision 28483)
+++ /branches/pap/psLib/src/fits/psFits.c	(revision 28484)
@@ -815,38 +815,38 @@
 {
     switch (datatype) {
-    case TBYTE:
+      case TBYTE:
         return PS_TYPE_U8;
-    case TSBYTE:
+      case TSBYTE:
         return PS_TYPE_S8;
-    case TSHORT:
+      case TSHORT:
         return PS_TYPE_S16;
-    case TUSHORT:
+      case TUSHORT:
         return PS_TYPE_U16;
-    case TLONG:
+      case TLONG:
         if (sizeof(long) == 8) {
             return PS_TYPE_S64;
         }
         // no break
-    case TINT:
+      case TINT:
         return PS_TYPE_S32;
-    case TULONG:
+      case TULONG:
         if (sizeof(unsigned long) == 8) {
             return PS_TYPE_U64;
         }
         // no break
-    case TUINT:
+      case TUINT:
         return PS_TYPE_U32;
-    case TLONGLONG:
+      case TLONGLONG:
         return PS_TYPE_S64;
-    case TFLOAT:
+      case TFLOAT:
         return PS_TYPE_F32;
-    case TDOUBLE:
+      case TDOUBLE:
         return PS_TYPE_F64;
-    case TLOGICAL:
+      case TLOGICAL:
         return PS_TYPE_BOOL;
-    default:
-        psError(PS_ERR_IO, true,
-                "Unknown FITS datatype, %d.",
-                datatype);
+      case TSTRING:
+        return PS_DATA_STRING;
+      default:
+        psError(PS_ERR_IO, true, "Unknown FITS datatype, %d.", datatype);
         return 0;
     }
Index: /branches/pap/psLib/src/fits/psFitsTable.c
===================================================================
--- /branches/pap/psLib/src/fits/psFitsTable.c	(revision 28483)
+++ /branches/pap/psLib/src/fits/psFitsTable.c	(revision 28484)
@@ -746,2 +746,259 @@
     return true;
 }
+
+
+psMetadata *psFitsReadTableAllColumns(const psFits *fits)
+{
+    PS_ASSERT_FITS_NON_NULL(fits, NULL);
+
+    if (!readTableCheck(fits)) {
+        return NULL;
+    }
+
+    int status = 0;                     // CFITSIO return status
+
+    long numRows = 0;                   // Number of rows in table
+    int numCols = 0;                    // Number of columns in table
+    fits_get_num_rows(fits->fd, &numRows, &status);
+    fits_get_num_cols(fits->fd, &numCols, &status);
+    if (psFitsError(status, true, "Failed to determine the size of the current HDU table.")) {
+        return NULL;
+    }
+
+    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;
+    }
+
+    psMetadata *table = psMetadataAlloc();     // Table to return
+    for (int col = 1; col <= numCols; col++) { // Fortran indexing
+        char name[FLEN_VALUE];           // Column name
+        if (hdutype == BINARY_TBL) {
+            fits_get_bcolparms(fits->fd, col, name,
+                               NULL, NULL, NULL, NULL, NULL, NULL, NULL, &status);
+        } else {
+            fits_get_acolparms(fits->fd, col, name,
+                               NULL, NULL, NULL, NULL, NULL, NULL, NULL, &status);
+        }
+
+        int cfitsioType = 0;             // Column type from CFITSIO
+        long repeat;                     // Number of repeats
+        fits_get_eqcoltype(fits->fd, col, &cfitsioType, &repeat, NULL, &status);
+        if (psFitsError(status, true, "Could not determine the column data for %s", name)) {
+            psFree(table);
+            return false;
+        }
+
+        psDataType pslibType = p_psFitsTypeFromCfitsio(cfitsioType); // Column type in psLib
+        if (pslibType == PS_DATA_STRING) {
+            // Strings
+            int width;                  // Width of strings
+            if (fits_get_col_display_width(fits->fd, col, &width, &status) != 0) {
+                psFitsError(status, true, "Could not determine the width of column %s", name);
+                psFree(table);
+                return NULL;
+            }
+            psArray *array = psArrayAlloc(numRows); // Array of strings from table
+            for (int i = 0; i < numRows; i++) {
+                array->data[i] = psStringAlloc(width);
+            }
+            fits_read_col_str(fits->fd, col, 1, 1, numRows, "", (char**)array->data, NULL, &status);
+            if (psFitsError(status, true, "Failed to read column %s", name)) {
+                psFree(array);
+                psFree(table);
+                return NULL;
+            }
+            if (!psMetadataAddArray(table, PS_LIST_TAIL, name, 0, NULL, array)) {
+                psError(PS_ERR_BAD_FITS, false, "Unable to add column %s to table", name);
+                psFree(array);
+                psFree(table);
+                return NULL;
+            }
+            psFree(array);
+        } else if (repeat == 1) {
+            // Single numbers
+            psVector* vector = psVectorAlloc(numRows, pslibType); // Vector from table
+            fits_read_col(fits->fd, cfitsioType, col, 1, 1, numRows, NULL,
+                          vector->data.U8, NULL, &status);
+            if (psFitsError(status, true, "Failed to read column %s", name)) {
+                psFree(vector);
+                psFree(table);
+                return NULL;
+            }
+            if (!psMetadataAddVector(table, PS_LIST_TAIL, name, 0, NULL, vector)) {
+                psError(PS_ERR_BAD_FITS, false, "Unable to add column %s to table", name);
+                psFree(vector);
+                psFree(table);
+                return NULL;
+            }
+            psFree(vector);
+        } else  {
+            // Vectors of numbers
+            psAssert(pslibType != PS_DATA_STRING, "Vectors of strings not handled");
+            psImage *image = psImageAlloc(repeat, numRows, pslibType); // Image to store vector of vectors
+            for (int row = 1, i = 0; row <= numRows; row++, i++) { // Fortran indexing for row
+                int anynul = 0;         // Any nulls in what was read?
+                fits_read_col(fits->fd, cfitsioType, col, row, 1, repeat, NULL,
+                              image->data.U8[i], &anynul, &status);
+                if (psFitsError(status, true, "Failed to read column %s row %d", name, row)) {
+                    psFree(image);
+                    psFree(table);
+                    return NULL;
+                }
+            }
+            if (!psMetadataAddImage(table, PS_LIST_TAIL, name, 0, NULL, image)) {
+                psError(PS_ERR_BAD_FITS, false, "Unable to add column %s to table", name);
+                psFree(image);
+                psFree(table);
+                return NULL;
+            }
+            psFree(image);
+        }
+    }
+
+    return table;
+}
+
+bool psFitsWriteTableAllColumns(psFits *fits, psMetadata *header, const psMetadata *table, const char *extname)
+{
+    PS_ASSERT_FITS_NON_NULL(fits, false);
+    PS_ASSERT_FITS_WRITABLE(fits, false);
+    PS_ASSERT_METADATA_NON_NULL(table, false);
+
+    psArray *columns = psListToArray(table->list); // Columns in psMetadataItems
+    int numCols = columns->n;                      // Number of columns
+    long numRows = 0;                              // Number of rows
+    psArray *names = psArrayAlloc(numCols);        // Column names
+    psArray *types = psArrayAlloc(numCols);        // Column types
+
+    for (int i = 0; i < numCols; i++) {
+        psMetadataItem *colItem = columns->data[i]; // Column
+        names->data[i] = psMemIncrRefCounter(colItem->name);
+
+        size_t size = 0;                   // Size of column
+        char tform = 0;                 // Type in character for FITS
+        long rows = 0;                   // Number of rows
+        switch (colItem->type) {
+          case PS_DATA_VECTOR:
+            size = 1;
+            psVector *vector = colItem->data.V; // Vector of interest
+            tform = getTForm(vector->type.type);
+            rows = vector->n;
+            break;
+          case PS_DATA_ARRAY:
+            tform = getTForm(PS_DATA_STRING);
+            psArray *array = colItem->data.V; // Array of interest
+            rows = array->n;
+            for (int i = 0; i < rows; i++) {
+                const char *string = array->data[i];
+                size = PS_MAX(size, strlen(string));
+            }
+            break;
+          case PS_DATA_IMAGE: ;
+            psImage *image = colItem->data.V; // Image of interest
+            tform = getTForm(image->type.type);
+            size = image->numCols;
+            rows = image->numRows;
+            break;
+          default:
+            psAbort("Unrecognised column type: %x", colItem->type);
+        }
+        psString type = NULL;           // Column type
+        psStringAppend(&type, "%zd%c", size, tform);
+        types->data[i] = type;
+        if (i == 0) {
+            numRows = rows;
+        } else if (numRows != rows) {
+            psError(PS_ERR_BAD_PARAMETER_SIZE, true, "Column %d has differing size: %ld vs %ld",
+                    i, rows, numRows);
+            psFree(names);
+            psFree(types);
+            psFree(columns);
+            return false;
+        }
+    }
+
+    // Create the table HDU
+    int numHDUs = psFitsGetSize(fits);  // Number of HDUs in file
+    int status = 0;                     // Status from cfitsio
+    if (numHDUs == 0) {
+        // We're creating the first extension
+        fits_create_tbl(fits->fd, BINARY_TBL, numRows, numCols, (char**)names->data, (char**)types->data,
+                        NULL, NULL, &status);
+    } else {
+        fits_insert_btbl(fits->fd, numRows, numCols, (char**)names->data, (char**)types->data,
+                         NULL, NULL, 0, &status);
+    }
+    psFree(names);
+    psFree(types);
+    if (psFitsError(status, true, "Unable to create FITS table with %d columns and %ld rows",
+                    numCols, numRows)) {
+        psFree(columns);
+        return false;
+    }
+
+    // Write header
+    if (header && !psFitsWriteHeader(fits, header)) {
+        psError(psErrorCodeLast(), false, "Unable to write FITS header.\n");
+        psFree(columns);
+        return false;
+    }
+    if (extname && strlen(extname) > 0 && !psFitsSetExtName(fits, extname)) {
+        psError(psErrorCodeLast(), false, "Unable to write FITS header extension name.\n");
+        psFree(columns);
+        return false;
+    }
+
+    // cfitsio requires that we write the data by columns --- urgh!
+    for (long col = 1, i = 0; col <= numCols; col++, i++) {      // Fortran indexing
+        psMetadataItem *colItem = columns->data[i]; // Column
+        switch (colItem->type) {
+          case PS_DATA_VECTOR: {
+              psVector *vector = colItem->data.V; // Vector
+              int cfitsioType;                    // Data type for cfitsio
+              p_psFitsTypeToCfitsio(vector->type.type, NULL, NULL, &cfitsioType);
+              fits_write_col(fits->fd, cfitsioType, col, 1, 1, numRows, vector->data.U8, &status);
+              break;
+          }
+          case PS_DATA_ARRAY: {
+              psArray *array = colItem->data.V; // Array of strings
+              fits_write_col_str(fits->fd, col, 1, 1, numRows, (char**)array->data, &status);
+              break;
+          }
+          case PS_DATA_IMAGE: {
+              psImage *image = colItem->data.V;                                        // Image of interest
+              psDataType type = image->type.type;                                      // Type of data
+              psVector *vector = psVectorAlloc(image->numCols * image->numRows, type); // Vector from image
+              if (!p_psImageCopyToRawBuffer(vector->data.U8, image, type)) {
+                  psError(psErrorCodeLast(), false, "Unable to copy image to buffer");
+                  psFree(columns);
+                  return false;
+              }
+              int cfitsioType;            // Data type for cfitsio
+              p_psFitsTypeToCfitsio(type, NULL, NULL, &cfitsioType);
+              fits_write_col(fits->fd, cfitsioType, col, 1, 1, image->numRows * image->numCols,
+                             vector->data.U8, &status);
+              break;
+          }
+          default:
+            psAbort("Unrecognised column type: %x", colItem->type);
+        }
+        // Check error status from writing column
+        if (psFitsError(status, true, "Unable to write column %ld of FITS table", col)) {
+            psFree(columns);
+            return false;
+        }
+    }
+    psFree(columns);
+
+    // 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;
+}
Index: /branches/pap/psLib/src/fits/psFitsTable.h
===================================================================
--- /branches/pap/psLib/src/fits/psFitsTable.h	(revision 28483)
+++ /branches/pap/psLib/src/fits/psFitsTable.h	(revision 28484)
@@ -62,4 +62,21 @@
 );
 
+/** Read all table columns.
+ *
+ * String columns are read into arrays, number columns are read into vectors.
+ */
+psMetadata *psFitsReadTableAllColumns(const psFits *fits // FITS file pointer
+                                      );
+
+/** Write all table columns.
+ *
+ * Uses the same format as for psFitsReadTableAllColumns.
+ */
+bool psFitsWriteTableAllColumns(
+                                psFits *fits, // FITS file pointer
+                                psMetadata *header, // Header to write, or NULL
+                                const psMetadata *table, // Table to write
+                                const char *extname      // Extension name, or NULL
+                                );
 
 /** Reads a whole FITS table.  The current HDU type must be either
Index: /branches/pap/psLib/src/imageops/psImageConvolve.c
===================================================================
--- /branches/pap/psLib/src/imageops/psImageConvolve.c	(revision 28483)
+++ /branches/pap/psLib/src/imageops/psImageConvolve.c	(revision 28484)
@@ -864,10 +864,8 @@
             PS_ARRAY_ADD_SCALAR(job->args, stop, PS_TYPE_S32);
             if (!psThreadJobAddPending(job)) {
-                psFree(job);
                 psFree(gaussNorm);
                 psFree(out);
                 return NULL;
             }
-            psFree(job);
         }
         if (!psThreadPoolWait(true)) {
@@ -1213,5 +1211,4 @@
                   if (!psThreadJobAddPending(job)) {
                       psError(PS_ERR_UNKNOWN, false, "Unable to smooth image");
-                      psFree(job);
                       psFree(calculation);
                       psFree(calcMask);
@@ -1219,5 +1216,4 @@
                       return false;
                   }
-                  psFree(job);
               }
               // wait here for the threaded jobs to finish (NOP if threading is not active)
@@ -1261,5 +1257,4 @@
                   if (!psThreadJobAddPending(job)) {
                       psError(PS_ERR_UNKNOWN, false, "Unable to smooth image");
-                      psFree(job);
                       psFree(calculation);
                       psFree(calcMask);
@@ -1267,5 +1262,4 @@
                       return false;
                   }
-                  psFree(job);
               }
 
@@ -1580,10 +1574,8 @@
             PS_ARRAY_ADD_SCALAR(job->args, 0x00, PS_TYPE_U8); // specify rows
             if (!psThreadJobAddPending(job)) {
-                psFree(job);
                 psFree(conv);
                 psFree(out);
                 return NULL;
             }
-            psFree(job);
         }
         if (!psThreadPoolWait(true)) {
@@ -1617,10 +1609,8 @@
             PS_ARRAY_ADD_SCALAR(job->args, 0xff, PS_TYPE_U8); // specify cols
             if (!psThreadJobAddPending(job)) {
-                psFree(job);
                 psFree(conv);
                 psFree(out);
                 return NULL;
             }
-            psFree(job);
         }
         if (!psThreadPoolWait(true)) {
Index: /branches/pap/psLib/src/imageops/psImageCovariance.c
===================================================================
--- /branches/pap/psLib/src/imageops/psImageCovariance.c	(revision 28483)
+++ /branches/pap/psLib/src/imageops/psImageCovariance.c	(revision 28484)
@@ -169,5 +169,4 @@
                     return NULL;
                 }
-                psFree(job);
             } else {
                 out->kernel[y][x] = imageCovarianceCalculate(covar, kernel, x, y);
@@ -334,5 +333,4 @@
                     return NULL;
                 }
-                psFree(job);
             } else {
                 out->kernel[y][x] = imageCovarianceBin(covar, bin, binVal, x, y);
Index: /branches/pap/psLib/src/sys/psAbort.c
===================================================================
--- /branches/pap/psLib/src/sys/psAbort.c	(revision 28483)
+++ /branches/pap/psLib/src/sys/psAbort.c	(revision 28484)
@@ -23,9 +23,48 @@
 #include <stdarg.h>
 #include <stdlib.h>
+#include <string.h>
 #include <unistd.h>
 
+#if defined(HAVE_BACKTRACE)
+#include <execinfo.h>
+#define BACKTRACE_BUFFER_SIZE 256       // Maximum size of backtrace
+#endif
+
 #include "psAbort.h"
+#include "psString.h"
+#include "psMemory.h"
 #include "psError.h"
 #include "psLogMsg.h"
+
+// Write backtrace to log
+static inline void psBacktrace(void)
+{
+#ifdef HAVE_BACKTRACE
+    void **bt = psAlloc(BACKTRACE_BUFFER_SIZE * sizeof(void *)); // Backtrace information
+    if (!bt) {
+        psLogMsg("psLib.sys", PS_LOG_ABORT, "Unable to allocate memory for backtrace");
+        return;
+    }
+    int size = backtrace(bt, BACKTRACE_BUFFER_SIZE);             // Size of backtrace
+    char **strings = backtrace_symbols((void *const *)bt, size);
+    psLogMsg("psLib.sys", PS_LOG_ABORT, "Backtrace depth: %d", size);
+    for (int i = 0; i < size; i++) {
+        // if the caller was an anon function then strchr won't
+        // find a '(' in the string and will return NULL
+        char *caller = strchr(strings[i], '(');
+        if (caller) {
+            // skip over the '('
+            caller++;
+            // find the end of the symbol name
+            size_t callerLength = abs(strchr(caller, '+') - caller);
+            psString name = psStringNCopy(caller, callerLength);
+            psLogMsg("psLib.sys", PS_LOG_ABORT, "Backtrace %d: %s", i, name);
+            psFree(name);
+        } else {
+            psLogMsg("psLib.sys", PS_LOG_ABORT, "Backtrace %d: (unknown)", i);
+        }
+    }
+#endif // ifdef HAVE_BACKTRACE
+}
 
 void p_psAbort(const char *file,
@@ -46,4 +85,6 @@
     // Clean up stack after variable arguement has been used
     va_end(argPtr);
+
+    psBacktrace();
 
     // Call system abort function to terminate program execution
@@ -72,4 +113,6 @@
     va_end(argPtr);
 
+    psBacktrace();
+
     // Call system abort function to terminate program execution
     fsync(psLogGetDestination());
Index: /branches/pap/psLib/src/sys/psLogMsg.c
===================================================================
--- /branches/pap/psLib/src/sys/psLogMsg.c	(revision 28483)
+++ /branches/pap/psLib/src/sys/psLogMsg.c	(revision 28484)
@@ -28,4 +28,5 @@
 #include <stdarg.h>
 #include <time.h>
+#include <sys/types.h>
 #include <unistd.h>
 #include <fcntl.h>
@@ -200,4 +201,10 @@
         return -1;
     }
+
+    if (lseek(fileD, 0, SEEK_END) == -1) {
+        psError(PS_ERR_IO, true, "Could not seek to end of file %s", dest);
+        return -1;
+    }
+
     return fileD;
 }
Index: /branches/pap/psLib/src/sys/psMemory.c
===================================================================
--- /branches/pap/psLib/src/sys/psMemory.c	(revision 28483)
+++ /branches/pap/psLib/src/sys/psMemory.c	(revision 28484)
@@ -452,4 +452,9 @@
     #endif
 
+    // XXX Looking at the reference counter is subject to a race condition because this function is generally
+    // not locked.  Normally this is not a problem because though we may increment and decrement references
+    // within a thread, we don't destroy the object completely (which is what we're checking for here).  It is
+    // the user's responsibility to protect against the complete destruction of memory either by not doing it
+    // or by locking on all reference changes for that memory.
     if (memBlock->refCounter < 1) {
         // using an unreferenced block of memory, are you?
Index: /branches/pap/psLib/src/sys/psThread.c
===================================================================
--- /branches/pap/psLib/src/sys/psThread.c	(revision 28483)
+++ /branches/pap/psLib/src/sys/psThread.c	(revision 28484)
@@ -7,4 +7,13 @@
 #include <unistd.h>
 #include <string.h>
+
+// Backtrace to help nail down bugs
+#ifdef HAVE_BACKTRACE
+#include <execinfo.h>
+#include <stdlib.h>
+#define BACKTRACE_BUFFER_SIZE 256       // Maximum size of backtrace
+static void **bt_buffer = NULL;         // Backtrace buffer
+static int bt_size = 0;                 // Backtrace buffer size
+#endif
 
 #include "psAssert.h"
@@ -29,4 +38,5 @@
 static psList *pending = NULL;          // queue of pending jobs
 static 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
 static psHash *tasks = NULL;            // List of defined tasks
@@ -89,5 +99,12 @@
 bool psThreadJobAddPending(psThreadJob *job)
 {
-    PS_ASSERT_THREAD_JOB_NON_NULL(job, false);
+    if (!job) {
+        // Asking for a no-op
+        return true;
+    }
+
+    psThreadTask *task = psHashLookup(tasks, job->type); // Task to execute job
+    psAssert(task, "Unable to find task %s", job->type);
+    psAssert(job->args->n == task->nArgs, "invalid number of arguments to %s", task->type);
 
     // if we failed to call psThreadPoolInit, or we called it with nThreads == 0,
@@ -100,9 +117,5 @@
         }
         psListAdd(done, PS_LIST_TAIL, job);
-
-        // find the corresponding task and run it
-        psThreadTask *task = psHashLookup(tasks, job->type); // Task to execute job
-        psAssert(task, "Unable to find task %s", job->type);
-        psAssert(job->args->n == task->nArgs, "invalid number of arguments to %s", task->type);
+        psFree(job);
         return task->function(job);
     }
@@ -113,4 +126,5 @@
     }
     psListAdd(pending, PS_LIST_TAIL, job);
+    psFree(job);
     psThreadUnlock();
 
@@ -209,6 +223,27 @@
 
         psThreadTask *task = psHashLookup(tasks, job->type); // Task to execute job
+#ifdef HAVE_BACKTRACE
+        if (!task && bt_buffer) {
+            psLogMsg("psLib.sys", PS_LOG_ABORT, "Backtrace of waiter:\n");
+            char **strings = backtrace_symbols((void *const *)bt_buffer, bt_size);
+            psLogMsg("psLib.sys", PS_LOG_ABORT, "Backtrace depth: %d", bt_size);
+            for (int i = 0; i < bt_size; i++) {
+                char *caller = strchr(strings[i], '(');
+                if (caller) {
+                    caller++;
+                    size_t callerLength = abs(strchr(caller, '+') - caller);
+                    psString name = psStringNCopy(caller, callerLength);
+                    psLogMsg("psLib.sys", PS_LOG_ABORT, "Backtrace %d: %s", i, name);
+                    psFree(name);
+                } else {
+                    psLogMsg("psLib.sys", PS_LOG_ABORT, "Backtrace %d: (unknown)", i);
+                }
+            }
+        }
+#endif
         psAssert(task, "Couldn't find thread task %s", job->type);
-        psAssert(job->args->n == task->nArgs, "invalid number of arguments to %s (%ld supplied, expected %d)", task->type, job->args->n, task->nArgs);
+        psAssert(job->args->n == task->nArgs,
+                 "invalid number of arguments to %s (%ld supplied, expected %d)",
+                 task->type, job->args->n, task->nArgs);
         bool status = task->function(job); // Status of executing task
 
@@ -234,5 +269,5 @@
 bool psThreadPoolInit(int nThreads)
 {
-    if (pool) {
+    if (pool || threads) {
         psAbort("psThreadsInit already called");
     }
@@ -245,8 +280,10 @@
 
     pool = psArrayAlloc(nThreads);
+    threads = psAlloc(nThreads * sizeof(pthread_t));
     for (int i = 0; i < nThreads; i++) {
-        psThread *thread = psThreadAlloc(); // Thread for pool
-        pthread_create(&thread->pt, NULL, psThreadLauncher, thread);
-        pool->data[i] = thread;
+        psThread *thread = pool->data[i] = psThreadAlloc(); // Thread for pool
+        if (pthread_create(&threads[i], NULL, psThreadLauncher, thread)) {
+            psAbort("Unable to create thread");
+        }
     }
     return true;
@@ -280,4 +317,12 @@
         return true;
     }
+
+#ifdef HAVE_BACKTRACE
+    if (bt_buffer) {
+        psFree(bt_buffer);
+    }
+    bt_buffer = psAlloc(BACKTRACE_BUFFER_SIZE * sizeof(void *));
+    bt_size = backtrace(bt_buffer, BACKTRACE_BUFFER_SIZE);
+#endif
 
     while (1) {
@@ -326,4 +371,5 @@
 bool psThreadPoolFinalize(void)
 {
+    psThreadLock();
     psFree(pending);
     pending = NULL;
@@ -335,4 +381,7 @@
     pool = NULL;
 
+    psFree(threads);
+    threads = NULL;
+
     psFree(tasks);
     tasks = NULL;
@@ -340,4 +389,12 @@
     psFree(tsd);
     tsd = NULL;
+
+#ifdef HAVE_BACKTRACE
+    if (bt_buffer) {
+        psFree(bt_buffer);
+    }
+#endif
+
+    psThreadUnlock();
 
     return true;
Index: /branches/pap/psLib/src/sys/psThread.h
===================================================================
--- /branches/pap/psLib/src/sys/psThread.h	(revision 28483)
+++ /branches/pap/psLib/src/sys/psThread.h	(revision 28484)
@@ -41,5 +41,4 @@
     bool busy;                          // Is the thread busy?
     bool fault;                         // Has the thread faulted?
-    pthread_t pt;                       // The thread itself
 } psThread;
 
@@ -74,10 +73,19 @@
 
 /// Add a pending job to the queue
+///
+/// This function swallows the provided job, so that the user no longer owns it.  This is because freeing the
+/// job is not thread-safe (its reference count is being changed within the threads) so we handle it ourselves
+/// and absolve the user from all responsibility.  If the user stores the job, he should only access it while
+/// threads are processing in code protected by psThreadLock/psThreadUnlock.
 bool psThreadJobAddPending(psThreadJob *job);
 
 /// Get a job off the queue of pending jobs
+///
+/// This function is not thread-safe.  Protect with psThreadLock/psThreadUnlock if threads are running.
 psThreadJob *psThreadJobGetPending(void);
 
 /// Get a job off the queue of done jobs
+///
+/// This function is not thread-safe.  Protect with psThreadLock/psThreadUnlock if threads are running.
 psThreadJob *psThreadJobGetDone(void);
 
@@ -116,5 +124,5 @@
 bool psThreadPoolFinalize(void);
 
-
+#if 0
 /// Add thread-specific data
 ///
@@ -135,5 +143,5 @@
 bool psThreadDataRemove(const char *name // Name of data
     );
-
+#endif
 
 /// @}
Index: /branches/pap/psLib/src/types/psTree.c
===================================================================
--- /branches/pap/psLib/src/types/psTree.c	(revision 28483)
+++ /branches/pap/psLib/src/types/psTree.c	(revision 28484)
@@ -342,6 +342,6 @@
         fprintf(fptr, " ");
     }
-    fprintf(fptr, "(");
     for (int i = 0; i < node->num; i++) {
+        fprintf(fptr, "%ld=(",node->contents[i]);
         psF64 *coords = tree->data->F64[node->contents[i]]; // Coordinates
         for (int j = 0; j < dim; j++) {
@@ -351,8 +351,5 @@
             }
         }
-        fprintf(fptr, ")");
-        if (i < node->num - 1) {
-            fprintf(fptr, " (");
-        }
+        fprintf(fptr, ") ");
     }
     fprintf(fptr, "\n");
@@ -433,11 +430,10 @@
         switch (dim) {
           case 2: {
-              // Haversine formula
+              // Haversine formula, modulo a factor of 1/2
               double dphi = coords->data.F64[1] - tree->data->F64[index][1];
-              double sindphi = sin(dphi / 2.0);
+              double haverPhi = 1.0 - cos(dphi);
               double dlambda = coords->data.F64[0] - tree->data->F64[index][0];
-              double sindlambda = sin(dlambda / 2.0);
-              return PS_SQR(sindphi) +
-                  cos(coords->data.F64[1]) * cos(tree->data->F64[index][1]) * PS_SQR(sindlambda);
+              double haverLambda = 1.0 - cos(dlambda);
+              return haverPhi + cos(coords->data.F64[1]) * cos(tree->data->F64[index][1]) * haverLambda;
           }
           default:
@@ -456,37 +452,43 @@
     int dim = tree->dim;                // Dimensionality
     double distance = 0.0;              // Distance to box
-    for (int i = 0; i < dim; i++) {
-        double minDiff = tree->min->F64[index][i] - coords->data.F64[i];
-        if (minDiff > 0) {
-            switch (tree->type) {
-              case PS_TREE_EUCLIDEAN:
+    switch (tree->type) {
+      case PS_TREE_EUCLIDEAN:
+        for (int i = 0; i < dim; i++) {
+            double minDiff = tree->min->F64[index][i] - coords->data.F64[i];
+            if (minDiff > 0) {
                 distance += PS_SQR(minDiff);
-                break;
-              case PS_TREE_SPHERICAL: {
-                  double sinDiff = sin(minDiff / 2.0);
-                  distance += PS_SQR(sinDiff);
-                  break;
-              }
-              default:
-                psAbort("Unrecognised type: %x", tree->type);
-            }
-            continue;
-        }
-        double maxDiff = coords->data.F64[i] - tree->max->F64[index][i];
-        if (maxDiff > 0) {
-            switch (tree->type) {
-              case PS_TREE_EUCLIDEAN:
+                continue;
+            }
+            double maxDiff = coords->data.F64[i] - tree->max->F64[index][i];
+            if (maxDiff > 0) {
                 distance += PS_SQR(maxDiff);
-                break;
-              case PS_TREE_SPHERICAL: {
-                  double sinDiff = sin(maxDiff / 2.0);
-                  distance += PS_SQR(sinDiff);
-                  break;
-              }
-              default:
-                psAbort("Unrecognised type: %x", tree->type);
-            }
-            continue;
-        }
+                continue;
+            }
+        }
+        break;
+      case PS_TREE_SPHERICAL: {
+          double ra = coords->data.F64[0], dec = coords->data.F64[1];                 // Coords of interest
+          double raMin = tree->min->F64[index][0], raMax = tree->max->F64[index][0]; // RA bounds
+          double decMin = tree->min->F64[index][1], decMax = tree->max->F64[index][1]; // Dec bounds
+
+          // Haversine formula, modulo a factor of 1/2
+          // This seems to deal with the wrap at RA=0=2pi, probably ascending the tree and descending on the
+          // other side of the wrap.
+          if (ra < raMin || ra > raMax) {
+              double ra1 = cos(ra - raMin), ra2 = cos(ra - raMax); // Options for RA distance
+              double raDist = PS_MAX(ra1, ra2);                    // Will give the smallest distance
+              double dec0 = (fabs(dec - decMin) < fabs(dec - decMax)) ? decMin : decMax; // Closest Dec limit
+              distance += cos(dec0) * cos(dec) * (1.0 - raDist);
+          }
+
+          if (dec < decMin || dec > decMax) {
+              double dec1 = cos(dec - decMin), dec2 = cos(dec - decMax); // Options for Dec distance
+              distance += 1.0 - PS_MAX(dec1, dec2);
+          }
+
+          break;
+      }
+      default:
+        psAbort("Unrecognised type: %x", tree->type);
     }
     return distance;
@@ -604,9 +606,7 @@
         // Using the square of the distance as the distance measure
         return PS_SQR(radius);
-      case PS_TREE_SPHERICAL: {
-          // Using a rearrangement of the Haversine formula
-          double sindist = sin(radius / 2.0);
-          return PS_SQR(sindist);
-      }
+      case PS_TREE_SPHERICAL:
+        // Using Haversine formula, modulo a factor of 1/2
+        return 1.0 - cos(radius);
       default:
         psAbort("Unrecognised type: %x", tree->type);
@@ -653,5 +653,5 @@
     long num = 0;                       // Number of points in circle
 
-    // This is essentially the same as psTreeNearest, except we're not allowed to prune
+    // This is essentially the same as psTreeNearest
 
     // Find the closest point in the leaf that contains the point of interest
@@ -667,4 +667,10 @@
         while (workIndex >= 0) {
             psTreeNode *node = work->data[workIndex];
+            if (treeBranchDistance(tree, node->index, coords) > distance) {
+                // No need to investigate
+                work->data[workIndex] = NULL;
+                workIndex--;
+                continue;
+            }
             if (node->left) {
                 // Branch node
@@ -696,5 +702,5 @@
 {
     for (int i = 0; i < leaf->num; i++) {
-        long index = leaf->contents[i]; // Index of point
+        psS64 index = leaf->contents[i]; // Index of point
         if (treeContentDistance(tree, index, coords) < distance) {
             psVectorAppend(result, index);
@@ -720,5 +726,5 @@
     psVector *result = psVectorAllocEmpty(4, PS_TYPE_S64); // Indices of points within match radius
 
-    // This is essentially the same as psTreeNearest, except we're not allowed to prune
+    // This is essentially the same as psTreeNearest, except pruning based on the search radius
 
     // Find the closest point in the leaf that contains the point of interest
@@ -734,4 +740,10 @@
         while (workIndex >= 0) {
             psTreeNode *node = work->data[workIndex];
+            if (treeBranchDistance(tree, node->index, coords) > distance) {
+                // No need to investigate
+                work->data[workIndex] = NULL;
+                workIndex--;
+                continue;
+            }
             if (node->left) {
                 // Branch node
@@ -801,4 +813,10 @@
         while (workIndex >= 0) {
             psTreeNode *node = work->data[workIndex];
+            if (treeBranchDistance(tree, node->index, coords) > distance) {
+                // No need to investigate
+                work->data[workIndex] = NULL;
+                workIndex--;
+                continue;
+            }
             if (node->left) {
                 // Branch node
Index: /branches/pap/psLib/test/types/tap_psTree.c
===================================================================
--- /branches/pap/psLib/test/types/tap_psTree.c	(revision 28483)
+++ /branches/pap/psLib/test/types/tap_psTree.c	(revision 28484)
@@ -3,6 +3,22 @@
 #include "pstap.h"
 
-#define NUM 1000000                      // Number of points
+#define NUM 1000000                     // Number of points
 #define SPHERICAL_DISTANCE 3.0          // Distance of interest for spherical test
+#define SEED 0                          // Random seed
+
+static double distance(double ra1, double dec1, double ra2, double dec2)
+{
+#if 0
+    // Traditional formula
+    return acos(sin(dec1) * sin(dec2) + cos(dec1) * cos(dec2) * cos(ra1 - ra2));
+#else
+    // Haversine formula: used in psTree
+    double dphi = dec1 - dec2;
+    double sindphi = sin(dphi/2.0);
+    double dlambda = ra1 - ra2;
+    double sindlambda = sin(dlambda/2.0);
+    return 2.0 * asin(sqrt(PS_SQR(sindphi) + cos(dec1) * cos(dec2) * PS_SQR(sindlambda)));
+#endif
+}
 
 int main(int argc, char *argv[])
@@ -54,5 +70,5 @@
             ok(closest->data.F64[0] == x->data.F64[bestIndex] &&
                closest->data.F64[1] == y->data.F64[bestIndex],
-               "correst coords: %lf,%lf(%lf) vs %lf,%lf(%lf)",
+               "correct coords: %lf,%lf(%lf) vs %lf,%lf(%lf)",
                closest->data.F64[0], closest->data.F64[1],
                sqrt(PS_SQR(closest->data.F64[0]) + PS_SQR(closest->data.F64[1])),
@@ -77,8 +93,8 @@
         psVector *dec = psVectorAlloc(NUM, PS_TYPE_F64);
 
-        psRandom *rng = psRandomAllocSpecific(PS_RANDOM_TAUS, 0);
+        psRandom *rng = psRandomAllocSpecific(PS_RANDOM_TAUS, SEED);
         for (int i = 0; i < NUM; i++) {
             // Using http://mathworld.wolfram.com/SpherePointPicking.html
-            ra->data.F64[i] = psRandomUniform(rng);
+            ra->data.F64[i] = psRandomUniform(rng) * 2.0 * M_PI;
             dec->data.F64[i] = acos(2.0 * psRandomUniform(rng) - 1.0) - M_PI_2;
         }
@@ -92,6 +108,10 @@
 
             psVector *coords = psVectorAlloc(2, PS_TYPE_F64);
-            coords->data.F64[0] = psRandomUniform(rng);
+#if 0
+            coords->data.F64[0] = psRandomUniform(rng) * 2.0 * M_PI;
             coords->data.F64[1] = acos(2.0 * psRandomUniform(rng) - 1.0) - M_PI_2;
+#else
+            psVectorInit(coords, 0);
+#endif
 
             psVector *indices = psTreeAllWithin(tree, coords, DEG_TO_RAD(SPHERICAL_DISTANCE));
@@ -102,41 +122,46 @@
             ok(psVectorSortInPlace(indices), "sorted indices");
 
+#if 0
+            for (long i = 0; i < indices->n; i++) {
+                long index = indices->data.S64[i];
+                double dist = distance(coords->data.F64[0], coords->data.F64[1],
+                                       ra->data.F64[index], dec->data.F64[index]);
+                diag("%ld (%lf,%lf) is in the list (%lf vs %lf)",
+                     index, ra->data.F64[index], dec->data.F64[index], RAD_TO_DEG(dist), SPHERICAL_DISTANCE);
+            }
+#endif
+
             bool allgood = true;        // All points in the appropriate place?
             double bestDistance = INFINITY; // Distance to best point
             long bestIndex = -1;        // Index of best point
+            long bad = 0;               // Number bad
             for (long i = 0, j = 0; i < NUM; i++) {
-#if 1
-                // Traditional formula
-                double dist = acos(sin(coords->data.F64[1]) * sin(dec->data.F64[i]) +
-                                   cos(coords->data.F64[1]) * cos(dec->data.F64[i]) *
-                                   cos(coords->data.F64[0] - ra->data.F64[i]));
-#else
-                // Haversine formula: used in psTree
-                double dphi = coords->data.F64[1] - dec->data.F64[i];
-                double sindphi = sin(dphi/2.0);
-                double dlambda = coords->data.F64[0] - ra->data.F64[i];
-                double sindlambda = sin(dlambda/2.0);
-                double dist = 2.0 * asin(sqrt(PS_SQR(sindphi) +
-                                              cos(coords->data.F64[1]) * cos(dec->data.F64[i]) *
-                                              PS_SQR(sindlambda)));
-#endif
-                                              if (dist < bestDistance) {
+                double dist = distance(coords->data.F64[0], coords->data.F64[1],
+                                       ra->data.F64[i], dec->data.F64[i]);
+                if (dist < bestDistance) {
                     bestDistance = dist;
                     bestIndex = i;
                 }
-                if (i == indices->data.S64[j]) {
+                if (j < indices->n && i == indices->data.S64[j]) {
                     j++;
                     if (dist > DEG_TO_RAD(SPHERICAL_DISTANCE)) {
-                        diag("%ld is in the list, but shouldn't be (%lf vs %lf)",
-                             i, RAD_TO_DEG(dist), SPHERICAL_DISTANCE);
+                        diag("%ld (%lf,%lf) is in the list, but shouldn't be (%lf vs %lf)",
+                             i, ra->data.F64[i], dec->data.F64[i], RAD_TO_DEG(dist), SPHERICAL_DISTANCE);
                         allgood = false;
+                        bad++;
+                    } else {
+#if 0
+                        diag("%ld (%lf,%lf) correctly identified in the list (%lf vs %lf)",
+                             i, ra->data.F64[i], dec->data.F64[i], RAD_TO_DEG(dist), SPHERICAL_DISTANCE);
+#endif
                     }
                 } else if (dist <= DEG_TO_RAD(SPHERICAL_DISTANCE)) {
-                    diag("%ld is not in the list, but should be (%lf vs %lf)",
-                         i, RAD_TO_DEG(dist), SPHERICAL_DISTANCE);
+                    diag("%ld (%lf,%lf) is not in the list, but should be (%lf vs %lf)",
+                         i, ra->data.F64[i], dec->data.F64[i], RAD_TO_DEG(dist), SPHERICAL_DISTANCE);
                     allgood = false;
+                    bad++;
                 }
             }
-            ok(allgood, "list is acurate");
+            ok(allgood, "list is accurate: %ld bad", bad);
             ok(bestIndex == closeIndex, "correct point: %ld vs %ld", closeIndex, bestIndex);
 
Index: /branches/pap/psModules/src/astrom/pmAstrometryWCS.c
===================================================================
--- /branches/pap/psModules/src/astrom/pmAstrometryWCS.c	(revision 28483)
+++ /branches/pap/psModules/src/astrom/pmAstrometryWCS.c	(revision 28484)
@@ -378,5 +378,5 @@
     // XXX make it optional to write out CDi_j terms, or other versions
     // apply CDELT1,2 (degrees / pixel) to yield PCi,j terms of order unity
-    if (!wcs->wcsCDkeys) { 
+    if (!wcs->wcsCDkeys) {
 
       double cdelt1 = wcs->cdelt1;
@@ -384,5 +384,5 @@
       psMetadataAddF64 (header, PS_LIST_TAIL, "CDELT1", PS_META_REPLACE, "", cdelt1);
       psMetadataAddF64 (header, PS_LIST_TAIL, "CDELT2", PS_META_REPLACE, "", cdelt2);
-      
+
       // test the PC00i00j varient:
       psMetadataAddF64 (header, PS_LIST_TAIL, "PC001001", PS_META_REPLACE, "", wcs->trans->x->coeff[1][0] / cdelt1); // == PC1_1
@@ -390,5 +390,5 @@
       psMetadataAddF64 (header, PS_LIST_TAIL, "PC002001", PS_META_REPLACE, "", wcs->trans->y->coeff[1][0] / cdelt1); // == PC2_1
       psMetadataAddF64 (header, PS_LIST_TAIL, "PC002002", PS_META_REPLACE, "", wcs->trans->y->coeff[0][1] / cdelt2); // == PC2_2
-      
+
       // Elixir-style polynomial terms
       // XXX currently, Elixir/DVO cannot accept mixed orders
@@ -398,18 +398,18 @@
       if (fitOrder > 1) {
         for (int i = 0; i <= fitOrder; i++) {
-	  for (int j = 0; j <= fitOrder; j++) {
-	    if (i + j < 2)
-	      continue;
-	    if (i + j > fitOrder)
-	      continue;
-	    sprintf (name, "PCA1X%1dY%1d", i, j);
-	    psMetadataAddF64 (header, PS_LIST_TAIL, name, PS_META_REPLACE, "", wcs->trans->x->coeff[i][j] / pow(cdelt1, i) / pow(cdelt2, j));
-	    sprintf (name, "PCA2X%1dY%1d", i, j);
-	    psMetadataAddF64 (header, PS_LIST_TAIL, name, PS_META_REPLACE, "", wcs->trans->y->coeff[i][j] / pow(cdelt1, i) / pow(cdelt2, j));
-	  }
+          for (int j = 0; j <= fitOrder; j++) {
+            if (i + j < 2)
+              continue;
+            if (i + j > fitOrder)
+              continue;
+            sprintf (name, "PCA1X%1dY%1d", i, j);
+            psMetadataAddF64 (header, PS_LIST_TAIL, name, PS_META_REPLACE, "", wcs->trans->x->coeff[i][j] / pow(cdelt1, i) / pow(cdelt2, j));
+            sprintf (name, "PCA2X%1dY%1d", i, j);
+            psMetadataAddF64 (header, PS_LIST_TAIL, name, PS_META_REPLACE, "", wcs->trans->y->coeff[i][j] / pow(cdelt1, i) / pow(cdelt2, j));
+          }
         }
         psMetadataAddS32 (header, PS_LIST_TAIL, "NPLYTERM", PS_META_REPLACE, "", fitOrder);
       }
-      
+
       // remove any existing 'CDi_j style' wcs keywords
       if (psMetadataLookup(header, "CD1_1")) {
@@ -419,4 +419,28 @@
         psMetadataRemoveKey(header, "CD2_2");
       }
+
+      // Remove 'CDi_jX' WCS keywords
+      psString cd11 = psStringCopy("CD1_1 ");
+      psString cd12 = psStringCopy("CD1_2 ");
+      psString cd21 = psStringCopy("CD2_1 ");
+      psString cd22 = psStringCopy("CD2_2 ");
+      for (char extra = 'A'; extra <= 'Z'; extra++) {
+          cd11[strlen(cd11)-1] = extra;
+          if (psMetadataLookup(header, cd11)) {
+              cd12[strlen(cd12)-1] = extra;
+              cd21[strlen(cd21)-1] = extra;
+              cd22[strlen(cd22)-1] = extra;
+              psMetadataRemoveKey(header, cd11);
+              psMetadataRemoveKey(header, cd12);
+              psMetadataRemoveKey(header, cd21);
+              psMetadataRemoveKey(header, cd22);
+          }
+      }
+      psFree(cd11);
+      psFree(cd12);
+      psFree(cd21);
+      psFree(cd22);
+
+
     } else {
 
@@ -427,8 +451,8 @@
 
       if (psMetadataLookup(header, "PC001001")) {
-	psMetadataRemoveKey(header, "PC001001");
-	psMetadataRemoveKey(header, "PC001002");
-	psMetadataRemoveKey(header, "PC002001");
-	psMetadataRemoveKey(header, "PC002002");
+        psMetadataRemoveKey(header, "PC001001");
+        psMetadataRemoveKey(header, "PC001002");
+        psMetadataRemoveKey(header, "PC002001");
+        psMetadataRemoveKey(header, "PC002002");
       }
     }
@@ -685,5 +709,5 @@
     }
 
-    // generate transform with the original orientation (does this rotate about 'center'?) 
+    // generate transform with the original orientation (does this rotate about 'center'?)
     psPlaneTransform *tpa2 = psPlaneTransformRotate (NULL, tpa1, -1.0*angle);
 
@@ -967,5 +991,5 @@
 
     // outFPA projection must be defined as the goal
-    
+
     // the output transformations are:
     // chip -> FPA : standard linear trans with needed rotation, etc
@@ -990,23 +1014,23 @@
         for (int i =  0; i < nSamples; i++) {
 
-	    psSphere srcSky;
-	    psPlane *srcChip = psPlaneAlloc();
-	    psPlane *dstTP = psPlaneAlloc();
+            psSphere srcSky;
+            psPlane *srcChip = psPlaneAlloc();
+            psPlane *dstTP = psPlaneAlloc();
 
             srcChip->x = bounds->x0 + (i * deltaX / nSamples);
             srcChip->y = y;
 
-	    psPlaneTransformApply (&srcFP, inChip->toFPA, srcChip);
-	    psPlaneTransformApply (&srcTP, inFPA->toTPA, &srcFP);
-	    psDeproject (&srcSky, &srcTP, inFPA->toSky);
-	    
-	    // fprintf (stderr, "%f %f | %f %f | %f %f | %f %f\n", srcChip->x, srcChip->y, srcFP.x, srcFP.y, srcTP.x, srcTP.y, srcSky.r*PS_DEG_RAD, srcSky.d*PS_DEG_RAD);
-
-	    psProject (dstTP, &srcSky, outFPA->toSky);
+            psPlaneTransformApply (&srcFP, inChip->toFPA, srcChip);
+            psPlaneTransformApply (&srcTP, inFPA->toTPA, &srcFP);
+            psDeproject (&srcSky, &srcTP, inFPA->toSky);
+
+            // fprintf (stderr, "%f %f | %f %f | %f %f | %f %f\n", srcChip->x, srcChip->y, srcFP.x, srcFP.y, srcTP.x, srcTP.y, srcSky.r*PS_DEG_RAD, srcSky.d*PS_DEG_RAD);
+
+            psProject (dstTP, &srcSky, outFPA->toSky);
 
             srcChip->x -= bounds->x0;
             srcChip->y -= bounds->y0;
-	    psArrayAdd (src, 100, srcChip);
-	    psArrayAdd (dst, 100, dstTP);
+            psArrayAdd (src, 100, srcChip);
+            psArrayAdd (dst, 100, dstTP);
 
             psFree(srcChip);  // drop our refs to s and d
@@ -1021,23 +1045,23 @@
     if (!psPlaneTransformFit(newToFPA, src, dst, 0, 0)) {
         psError(PS_ERR_UNKNOWN, false, "linear fit to transform failed");
-	psFree(src);
-	psFree(dst);
+        psFree(src);
+        psFree(dst);
         return NULL;
     }
-    
+
 # if (0)
     for (int i = 0; i < src->n; i++) {
-	
-	psSphere srcSky, dstSky;
-	psPlane *srcChip = src->data[i];
-	psPlane *dstTP   = dst->data[i];
-
-	psPlaneTransformApply (&srcFP, newToFPA, srcChip);
-	psDeproject (&srcSky, &srcFP, outFPA->toSky);
-	psDeproject (&dstSky, dstTP, outFPA->toSky);
-
-	double dX = (srcSky.r*PS_DEG_RAD - dstSky.r*PS_DEG_RAD)*3600.0;
-	double dY = (srcSky.d*PS_DEG_RAD - dstSky.d*PS_DEG_RAD)*3600.0;
-	fprintf (stderr, "%f %f | %f %f | %f %f | %f %f | %f %f | %f %f\n", dX, dY, srcChip->x, srcChip->y, srcFP.x, srcFP.y, dstTP->x, dstTP->y, srcSky.r*PS_DEG_RAD, srcSky.d*PS_DEG_RAD, dstSky.r*PS_DEG_RAD, dstSky.d*PS_DEG_RAD);
+
+        psSphere srcSky, dstSky;
+        psPlane *srcChip = src->data[i];
+        psPlane *dstTP   = dst->data[i];
+
+        psPlaneTransformApply (&srcFP, newToFPA, srcChip);
+        psDeproject (&srcSky, &srcFP, outFPA->toSky);
+        psDeproject (&dstSky, dstTP, outFPA->toSky);
+
+        double dX = (srcSky.r*PS_DEG_RAD - dstSky.r*PS_DEG_RAD)*3600.0;
+        double dY = (srcSky.d*PS_DEG_RAD - dstSky.d*PS_DEG_RAD)*3600.0;
+        fprintf (stderr, "%f %f | %f %f | %f %f | %f %f | %f %f | %f %f\n", dX, dY, srcChip->x, srcChip->y, srcFP.x, srcFP.y, dstTP->x, dstTP->y, srcSky.r*PS_DEG_RAD, srcSky.d*PS_DEG_RAD, dstSky.r*PS_DEG_RAD, dstSky.d*PS_DEG_RAD);
 
     }
Index: /branches/pap/psModules/src/camera/pmFPAfileIO.c
===================================================================
--- /branches/pap/psModules/src/camera/pmFPAfileIO.c	(revision 28483)
+++ /branches/pap/psModules/src/camera/pmFPAfileIO.c	(revision 28484)
@@ -759,5 +759,6 @@
     psString realName = pmConfigConvertFilename(file->filename, config, create, false);
     if (!realName) {
-        psError(PS_ERR_IO, false, "failed to determine real name from template for %s\n", file->filename);
+        psError(psErrorCodeLast(), false, "failed to determine real name from template for %s\n",
+                file->filename);
         return false;
     }
Index: /branches/pap/psModules/src/camera/pmReadoutFake.c
===================================================================
--- /branches/pap/psModules/src/camera/pmReadoutFake.c	(revision 28483)
+++ /branches/pap/psModules/src/camera/pmReadoutFake.c	(revision 28484)
@@ -303,9 +303,7 @@
 
                 if (!psThreadJobAddPending(job)) {
-                    psFree(job);
                     psFree(groups);
                     return false;
                 }
-                psFree(job);
             }
             if (!psThreadPoolWait(true)) {
Index: /branches/pap/psModules/src/config/pmConfig.c
===================================================================
--- /branches/pap/psModules/src/config/pmConfig.c	(revision 28483)
+++ /branches/pap/psModules/src/config/pmConfig.c	(revision 28484)
@@ -306,5 +306,5 @@
     *config = psMetadataConfigRead(NULL, &numBadLines, realName, true);
     if (numBadLines > 0) {
-        psError(PM_ERR_CONFIG, false, "%d bad lines in %s configuration file (%s)",
+        psError(PM_ERR_CONFIG, true, "%d bad lines in %s configuration file (%s)",
                 numBadLines, description, realName);
         psFree (realName);
@@ -1751,5 +1751,5 @@
     psMetadataItem *item = psMetadataLookup(camera, "FILERULES"); // Item with the file rule of interest
     if (!item) {
-        psError(PM_ERR_CONFIG, false, "Unable to find FILERULES in the camera configuration.");
+        psError(PM_ERR_CONFIG, true, "Unable to find FILERULES in the camera configuration.");
         return NULL;
     }
@@ -1771,5 +1771,5 @@
     }
 
-    return psMetadataLookupMetadata(&mdok, filerules, realname);
+    return psMetadataLookupMetadata(NULL, filerules, realname);
 }
 
Index: /branches/pap/psModules/src/config/pmConfigMask.c
===================================================================
--- /branches/pap/psModules/src/config/pmConfigMask.c	(revision 28483)
+++ /branches/pap/psModules/src/config/pmConfigMask.c	(revision 28484)
@@ -8,4 +8,12 @@
 
 #include "pmConfigMask.h"
+
+// Structure to hold the properties of a mask value
+typedef struct {
+    char *badMaskName;                  // name for "bad" (i.e., mask me please) pixels
+    char *fallbackName;                 // Fallback name in case a bad mask name is not defined
+    psImageMaskType defaultMaskValue;   // Default value in case a bad mask name and its fallback are not defined
+    bool isBad; // include this value as part of the MASK.VALUE entry (generically bad)
+} pmConfigMaskInfo;
 
 static pmConfigMaskInfo masks[] = {
Index: /branches/pap/psModules/src/config/pmConfigMask.h
===================================================================
--- /branches/pap/psModules/src/config/pmConfigMask.h	(revision 28483)
+++ /branches/pap/psModules/src/config/pmConfigMask.h	(revision 28484)
@@ -20,12 +20,4 @@
 /// @{
 
-// structure to hold the properties of a mask value
-typedef struct {
-    char *badMaskName;			// name for "bad" (i.e., mask me please) pixels
-    char *fallbackName;			// Fallback name in case a bad mask name is not defined
-    psImageMaskType defaultMaskValue;	// Default value in case a bad mask name and its fallback are not defined
-    bool isBad;	// include this value as part of the MASK.VALUE entry (generically bad)
-} pmConfigMaskInfo;
-
 // pmConfigMaskSetInMetadata examines named mask values and set the bits for maskValue and
 // markValue.  Ensures that the below-named mask values are set, and calculates the mask value
@@ -33,6 +25,6 @@
 // name is not found, or the default values if the fallback name is not found.
 bool pmConfigMaskSetInMetadata(psImageMaskType *outMaskValue, // Value of MASK.VALUE, returned
-			       psImageMaskType *outMarkValue, // Value of MARK.VALUE, returned
-			       psMetadata *source  // Source of mask bits
+                               psImageMaskType *outMarkValue, // Value of MARK.VALUE, returned
+                               psMetadata *source  // Source of mask bits
   );
 
@@ -40,9 +32,9 @@
 // Get a mask value by name(s)
 psImageMaskType pmConfigMaskGetFromMetadata(psMetadata *source, // Source of masks
-					    const char *masks // Mask values to get
+                                            const char *masks // Mask values to get
   );
 
 
-// lookup an image mask value by name from a psMetadata, without requiring the entry to 
+// lookup an image mask value by name from a psMetadata, without requiring the entry to
 // be of type psImageMaskType, but verifying that it will fit in psImageMaskType
 psImageMaskType psMetadataLookupImageMaskFromGeneric (bool *status, const psMetadata *md, const char *name);
@@ -50,5 +42,5 @@
 // Remove from the header keywords starting with the provided string
 int pmConfigMaskRemoveHeaderKeywords(psMetadata *header, // Header from which to remove keywords
-				     const char *start // Remove keywords that start with this string
+                                     const char *start // Remove keywords that start with this string
   );
 
Index: /branches/pap/psModules/src/detrend/pmBias.c
===================================================================
--- /branches/pap/psModules/src/detrend/pmBias.c	(revision 28483)
+++ /branches/pap/psModules/src/detrend/pmBias.c	(revision 28484)
@@ -144,8 +144,6 @@
 
             if (!psThreadJobAddPending(job)) {
-                psFree(job);
                 return false;
             }
-            psFree(job);
         } else if (!pmBiasSubtractScan(in, sub, scale, xOffset, yOffset, rowStart, rowStop)) {
             psError(PS_ERR_UNKNOWN, false, "Unable to apply bias correction.");
Index: /branches/pap/psModules/src/detrend/pmDark.c
===================================================================
--- /branches/pap/psModules/src/detrend/pmDark.c	(revision 28483)
+++ /branches/pap/psModules/src/detrend/pmDark.c	(revision 28484)
@@ -587,10 +587,8 @@
 
             if (!psThreadJobAddPending (job)) {
-                psFree(job);
                 psFree(orders);
                 psFree(values);
                 return false;
             }
-            psFree(job);
         } else if (!pmDarkApplyScan(readout, dark, orders, values, bad, doNorm, norm, rowStart, rowStop)) {
             psError(PS_ERR_UNKNOWN, false, "Unable to apply dark.");
Index: /branches/pap/psModules/src/detrend/pmFlatField.c
===================================================================
--- /branches/pap/psModules/src/detrend/pmFlatField.c	(revision 28483)
+++ /branches/pap/psModules/src/detrend/pmFlatField.c	(revision 28484)
@@ -150,8 +150,6 @@
 
           if (!psThreadJobAddPending(job)) {
-              psFree(job);
               return false;
           }
-          psFree(job);
       } else if (!pmFlatFieldScan(inImage, inMask, inVar, flatImage, flatMask, badFlat,
                                   xOffset, yOffset, rowStart, rowStop)) {
Index: anches/pap/psModules/src/detrend/pmGainTweak.c
===================================================================
--- /branches/pap/psModules/src/detrend/pmGainTweak.c	(revision 28483)
+++ 	(revision )
@@ -1,59 +1,0 @@
-#include <stdio.h>
-#include <pslib.h>
-
-#include "pmFPA.h"
-
-#include "pmGainTweak.h"
-
-bool pmGainTweak(const pmCell *cell)
-{
-    PS_ASSERT_PTR_NON_NULL(cell, false);
-    pmChip *chip = cell->parent;
-    PS_ASSERT_PTR_NON_NULL(chip, false);
-
-    int numCells = chip->cells;         // Number of cells
-    psVector *gains = psVectorAlloc(numCells, PS_TYPE_F32); // Gain for each cell
-    psVector *mask = psVectorAlloc(numCells, PS_TYPE_VECTOR_MASK); // Mask for gains
-    psVectorInit(mask, 0);
-    int numGood = 0;                    // Number of good gains
-
-   for (int i = 0; i < numCells; i++) {
-        pmCell *otherCell = chip->cells->data[i]; // A different cell
-        if (otherCell == cell) {
-            mask->data.PS_TYPE_VECTOR_MASK_DATA[i] = 0xFF;
-            continue;
-        }
-        float gain = psMetadataLookupF32(NULL, otherCell->concepts, "CELL.GAIN"); // Gain for cell
-        gains->data.F32[i] = gain;
-        if (!isfinite(gains->data.F32[i])) {
-            mask->data.PS_TYPE_VECTOR_MASK_DATA[i] = 0xFF;
-        } else {
-            numGood++;
-        }
-   }
-
-   if (numGood == 0) {
-       psError(PM_ERR_DETREND, true, "No other cell gains available with which to tweak gain.");
-       psFree(gains);
-       psFree(mask);
-       return false;
-   }
-
-   psStats *stats = psStatsAlloc(PS_STAT_SAMPLE_MEDIAN); // Statistics
-   if (!psVectorStats(stats, gains, NULL, mask, 0xFF)) {
-       psError(psErrorCodeLast(), false, "Unable to determine target gain");
-       psFree(stats);
-       psFree(gains);
-       psFree(mask);
-       return false;
-   }
-   float target = stats->sampleMedian;  // Target gain
-   psFree(stats);
-   psFree(gains);
-   psFree(mask);
-
-   psMetadataItem *item = psMetadataLookup(cell->concepts, "CELL.GAIN");
-   item->data.F32 = target;
-
-   return true;
-}
Index: /branches/pap/psModules/src/detrend/pmShutterCorrection.c
===================================================================
--- /branches/pap/psModules/src/detrend/pmShutterCorrection.c	(revision 28483)
+++ /branches/pap/psModules/src/detrend/pmShutterCorrection.c	(revision 28484)
@@ -351,10 +351,10 @@
     psStats *resStats = psStatsAlloc (PS_STAT_SAMPLE_MEDIAN | PS_STAT_SAMPLE_STDEV);
     if (!psVectorStats (rawStats, counts, NULL, NULL, 0)) {
-	psError(PS_ERR_UNKNOWN, false, "failure to measure stats");
-	return NULL;
+        psError(PS_ERR_UNKNOWN, false, "failure to measure stats");
+        return NULL;
     }
     if (!psVectorStats (resStats, resid, NULL, NULL, 0)) {
-	psError(PS_ERR_UNKNOWN, false, "failure to measure stats");
-	return NULL;
+        psError(PS_ERR_UNKNOWN, false, "failure to measure stats");
+        return NULL;
     }
 
@@ -794,8 +794,6 @@
 
                 if (!psThreadJobAddPending(job)) {
-                    psFree(job);
                     return false;
                 }
-                psFree(job);
             } else if (!pmShutterCorrectionApplyScan(image, mask, var, shutterImage, exptime, blank,
                                                      rowStart, rowStop)) {
@@ -1017,13 +1015,13 @@
 
         if (corr) {
-	    psTrace("psModules.detrend", 5, "Shutter correction fit: scale: %f, offset: %f, offref: %f\n", corr->scale, corr->offset, corr->offref);
-	    if (isfinite(corr->offref) && corr->valid) {
-		psTrace("psModules.detrend", 5, "Sample reference value: %f\n", corr->offref);
-		meanRef += corr->offref;
-		numGood++;
-	    }
+            psTrace("psModules.detrend", 5, "Shutter correction fit: scale: %f, offset: %f, offref: %f\n", corr->scale, corr->offset, corr->offref);
+            if (isfinite(corr->offref) && corr->valid) {
+                psTrace("psModules.detrend", 5, "Sample reference value: %f\n", corr->offref);
+                meanRef += corr->offref;
+                numGood++;
+            }
         } else {
-	    psTrace("psModules.detrend", 5, "failed Shutter correction fit\n");
-	}
+            psTrace("psModules.detrend", 5, "failed Shutter correction fit\n");
+        }
 
         psFree(corr);
Index: /branches/pap/psModules/src/imcombine/pmPSFEnvelope.c
===================================================================
--- /branches/pap/psModules/src/imcombine/pmPSFEnvelope.c	(revision 28483)
+++ /branches/pap/psModules/src/imcombine/pmPSFEnvelope.c	(revision 28484)
@@ -33,5 +33,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
@@ -235,5 +235,5 @@
 
             // Get the radius
-            pmModel *model = pmModelFromPSFforXY(psf, x, y, PEAK_FLUX); // Model for source
+            pmModel *model = pmModelFromPSFforXY(psf, source->peak->xf, source->peak->yf, PEAK_FLUX); // Model for source
             if (!model || (model->flags & MODEL_MASK)) {
                 continue;
@@ -321,5 +321,5 @@
     numFakes = fakes->n;
 
-    if (numFakes == 0.0) {
+    if (numFakes == 0) {
         psError(PS_ERR_UNKNOWN, false, "No fake sources are suitable for PSF fitting.");
         psFree(fakes);
Index: /branches/pap/psModules/src/imcombine/pmStackReject.c
===================================================================
--- /branches/pap/psModules/src/imcombine/pmStackReject.c	(revision 28483)
+++ /branches/pap/psModules/src/imcombine/pmStackReject.c	(revision 28484)
@@ -289,10 +289,8 @@
                     PS_ARRAY_ADD_SCALAR(args, poorFrac, PS_TYPE_F32);
                     if (!psThreadJobAddPending(job)) {
-                        psFree(job);
                         psFree(source);
                         psFree(target);
                         return NULL;
                     }
-                    psFree(job);
                 } else if (!stackRejectGrow(target, source, kernels, numCols, numRows,
                                             i, xSubMax, j, ySubMax, poorFrac)) {
Index: /branches/pap/psModules/src/imcombine/pmSubtraction.c
===================================================================
--- /branches/pap/psModules/src/imcombine/pmSubtraction.c	(revision 28483)
+++ /branches/pap/psModules/src/imcombine/pmSubtraction.c	(revision 28484)
@@ -1291,8 +1291,6 @@
 
                 if (!psThreadJobAddPending(job)) {
-                    psFree(job);
                     return false;
                 }
-                psFree(job);
             } else {
                 subtractionConvolvePatch(numCols, numRows, x0, y0, out1, out2, convMask, ro1, ro2,
Index: /branches/pap/psModules/src/imcombine/pmSubtractionEquation.c
===================================================================
--- /branches/pap/psModules/src/imcombine/pmSubtractionEquation.c	(revision 28483)
+++ /branches/pap/psModules/src/imcombine/pmSubtractionEquation.c	(revision 28484)
@@ -860,8 +860,6 @@
             PS_ARRAY_ADD_SCALAR(job->args, mode, PS_TYPE_S32);
             if (!psThreadJobAddPending(job)) {
-                psFree(job);
                 return false;
             }
-            psFree(job);
         } else {
             pmSubtractionCalculateEquationStamp(stamps, kernels, i, mode);
Index: /branches/pap/psModules/src/imcombine/pmSubtractionMatch.c
===================================================================
--- /branches/pap/psModules/src/imcombine/pmSubtractionMatch.c	(revision 28483)
+++ /branches/pap/psModules/src/imcombine/pmSubtractionMatch.c	(revision 28484)
@@ -1060,8 +1060,6 @@
             PS_ARRAY_ADD_SCALAR(job->args, bg2, PS_TYPE_F32);
             if (!psThreadJobAddPending(job)) {
-                psFree(job);
                 return false;
             }
-            psFree(job);
         } else {
             if (!pmSubtractionOrderStamp(ratios, mask, stamps, models, modelSums, i, bg1, bg2)) {
Index: /branches/pap/psModules/src/objects/pmPSF_IO.c
===================================================================
--- /branches/pap/psModules/src/objects/pmPSF_IO.c	(revision 28483)
+++ /branches/pap/psModules/src/objects/pmPSF_IO.c	(revision 28484)
@@ -174,5 +174,14 @@
     PS_ASSERT_PTR_NON_NULL(chip, false);
 
-    if (!pmPSFmodelWrite(chip->analysis, view, file, config)) {
+    // We need the readout as well, because that has the PSF analysis data (e.g., clumps)
+    // There is only one, because photometry is done on chip-mosaicked data.
+    pmFPAview *roView = pmFPAviewAlloc(0); // View to readout
+    *roView = *view;
+    roView->cell = 0;
+    roView->readout = 0;
+    pmReadout *ro = pmFPAviewThisReadout(roView, chip->parent); // Readout with analysis data
+    psFree(roView);
+
+    if (!pmPSFmodelWrite(chip->analysis, ro ? ro->analysis : NULL, view, file, config)) {
         psError(psErrorCodeLast(), false, "Failed to write PSF for chip");
         return false;
@@ -189,5 +198,5 @@
 // else
 //   - psf table (+header) : FITS Table
-bool pmPSFmodelWrite (psMetadata *analysis, const pmFPAview *view,
+bool pmPSFmodelWrite (const psMetadata *chipAnalysis, const psMetadata *roAnalysis, const pmFPAview *view,
                       pmFPAfile *file, pmConfig *config)
 {
@@ -198,7 +207,10 @@
     char *headName, *tableName, *residName;
 
-    if (!analysis) {
+    if (!chipAnalysis) {
         psError(PM_ERR_PROG, true, "No analysis metadata for chip.");
         return false;
+    }
+    if (!roAnalysis) {
+        psWarning("No analysis metadata for PSF, clump parameters cannot be saved.");
     }
 
@@ -314,5 +326,5 @@
 
     // select the psf of interest
-    pmPSF *psf = psMetadataLookupPtr (&status, analysis, "PSPHOT.PSF");
+    pmPSF *psf = psMetadataLookupPtr (&status, chipAnalysis, "PSPHOT.PSF");
     if (!psf) {
         psError(PM_ERR_PROG, true, "missing PSF for this analysis metadata");
@@ -346,25 +358,27 @@
 
         // we now save clump parameters for each region : need to save all of those
-        int nRegions = psMetadataLookupS32 (&status, analysis, "PSF.CLUMP.NREGIONS");
-        psMetadataAddS32 (header, PS_LIST_TAIL, "PSF_CLN", PS_META_REPLACE, "number of psf clump regions", nRegions);
-        for (int i = 0; i < nRegions; i++) {
-            char regionName[64];
-            snprintf (regionName, 64, "PSF.CLUMP.REGION.%03d", i);
-            psMetadata *regionMD = psMetadataLookupPtr (&status, analysis, regionName);
-
-            psfClump.X  = psMetadataLookupF32 (&status, regionMD, "PSF.CLUMP.X");   assert (status);
-            psfClump.Y  = psMetadataLookupF32 (&status, regionMD, "PSF.CLUMP.Y");   assert (status);
-            psfClump.dX = psMetadataLookupF32 (&status, regionMD, "PSF.CLUMP.DX");  assert (status);
-            psfClump.dY = psMetadataLookupF32 (&status, regionMD, "PSF.CLUMP.DY");  assert (status);
-
-            char key[16];
-            snprintf (key, 9, "CLX_%03d", i);
-            psMetadataAddF32 (header, PS_LIST_TAIL, key, PS_META_REPLACE, "psf clump center", psfClump.X);
-            snprintf (key, 9, "CLY_%03d", i);
-            psMetadataAddF32 (header, PS_LIST_TAIL, key, PS_META_REPLACE, "psf clump center", psfClump.Y);
-            snprintf (key, 9, "CLDX_%03d", i);
-            psMetadataAddF32 (header, PS_LIST_TAIL, key, PS_META_REPLACE, "psf clump size", psfClump.dX);
-            snprintf (key, 9, "CLDY_%03d", i);
-            psMetadataAddF32 (header, PS_LIST_TAIL, key, PS_META_REPLACE, "psf clump size", psfClump.dY);
+        if (roAnalysis) {
+            int nRegions = psMetadataLookupS32 (&status, roAnalysis, "PSF.CLUMP.NREGIONS");
+            psMetadataAddS32 (header, PS_LIST_TAIL, "PSF_CLN", PS_META_REPLACE, "number of psf clump regions", nRegions);
+            for (int i = 0; i < nRegions; i++) {
+                char regionName[64];
+                snprintf (regionName, 64, "PSF.CLUMP.REGION.%03d", i);
+                psMetadata *regionMD = psMetadataLookupPtr (&status, roAnalysis, regionName);
+
+                psfClump.X  = psMetadataLookupF32 (&status, regionMD, "PSF.CLUMP.X");   assert (status);
+                psfClump.Y  = psMetadataLookupF32 (&status, regionMD, "PSF.CLUMP.Y");   assert (status);
+                psfClump.dX = psMetadataLookupF32 (&status, regionMD, "PSF.CLUMP.DX");  assert (status);
+                psfClump.dY = psMetadataLookupF32 (&status, regionMD, "PSF.CLUMP.DY");  assert (status);
+
+                char key[16];
+                snprintf (key, 9, "CLX_%03d", i);
+                psMetadataAddF32 (header, PS_LIST_TAIL, key, PS_META_REPLACE, "psf clump center", psfClump.X);
+                snprintf (key, 9, "CLY_%03d", i);
+                psMetadataAddF32 (header, PS_LIST_TAIL, key, PS_META_REPLACE, "psf clump center", psfClump.Y);
+                snprintf (key, 9, "CLDX_%03d", i);
+                psMetadataAddF32 (header, PS_LIST_TAIL, key, PS_META_REPLACE, "psf clump size", psfClump.dX);
+                snprintf (key, 9, "CLDY_%03d", i);
+                psMetadataAddF32 (header, PS_LIST_TAIL, key, PS_META_REPLACE, "psf clump size", psfClump.dY);
+            }
         }
 
@@ -492,23 +506,23 @@
 
         // write the residuals as planes of the image
-	psArray *images = psArrayAllocEmpty (1);
-	psArrayAdd (images, 1, psf->residuals->Ro);  // z = 0 is Ro
+        psArray *images = psArrayAllocEmpty (1);
+        psArrayAdd (images, 1, psf->residuals->Ro);  // z = 0 is Ro
 
         if (psf->residuals->Rx) {
             psArrayAdd (images, 1, psf->residuals->Rx);
             psArrayAdd (images, 1, psf->residuals->Ry);
-	}
-
-	// note that all N plane are implicitly of the same type, so we convert the mask
-	if (psf->residuals->mask) {
-	    psImage *mask = psImageCopy (NULL, psf->residuals->mask, psf->residuals->Ro->type.type);
-	    psArrayAdd (images, 1, mask);
-	    psFree (mask);
-	}
-
-	// psFitsWriteImageCube (file->fits, header, images, residName);
-	// psFree (images);
-
-	if (!psFitsWriteImageCube (file->fits, header, images, residName)) {
+        }
+
+        // note that all N plane are implicitly of the same type, so we convert the mask
+        if (psf->residuals->mask) {
+            psImage *mask = psImageCopy (NULL, psf->residuals->mask, psf->residuals->Ro->type.type);
+            psArrayAdd (images, 1, mask);
+            psFree (mask);
+        }
+
+        // psFitsWriteImageCube (file->fits, header, images, residName);
+        // psFree (images);
+
+        if (!psFitsWriteImageCube (file->fits, header, images, residName)) {
             psError(psErrorCodeLast(), false, "Unable to write PSF residuals.");
             psFree(images);
@@ -728,5 +742,29 @@
     PS_ASSERT_PTR_NON_NULL(file->fpa, false);
 
-    if (!pmPSFmodelRead (chip->analysis, view, file, config)) {
+    // We need the readout as well, because that has the PSF analysis data (e.g., clumps)
+    // There may be only one, because photometry is done on chip-mosaicked data.
+    if (chip->cells->n != 1) {
+        psError(PM_ERR_PROG, true, "Chip to receive PSF has %ld cells (should be only one)",
+                chip->cells->n);
+        return false;
+    }
+    pmCell *cell = chip->cells->data[0]; // Cell to receive PSF
+    pmReadout *ro = NULL;                // Readout to receive PSF
+    if (cell->readouts->n == 0) {
+        ro = pmReadoutAlloc(cell);
+        psFree(ro);                     // Drop reference
+    } else if (cell->readouts->n != 1) {
+        psError(PM_ERR_PROG, true, "Cell to receive PSF has %ld readouts (should be only one)",
+                cell->readouts->n);
+        return false;
+    } else {
+        ro = cell->readouts->data[0];
+    }
+    PM_ASSERT_READOUT_NON_NULL(ro, false);
+    if (!ro->analysis) {
+        ro->analysis = psMetadataAlloc();
+    }
+
+    if (!pmPSFmodelRead(chip->analysis, ro->analysis, view, file, config)) {
         psError(psErrorCodeLast(), false, "Failed to write PSF for chip");
         return false;
@@ -737,6 +775,7 @@
 // for each Readout (ie, analysed image), we write out: header + table with PSF model parameters,
 // and header + image for the PSF residual images
-bool pmPSFmodelRead (psMetadata *analysis, const pmFPAview *view, pmFPAfile *file, const pmConfig *config)
+bool pmPSFmodelRead (psMetadata *chipAnalysis, psMetadata *roAnalysis, const pmFPAview *view, pmFPAfile *file, const pmConfig *config)
 {
+    PS_ASSERT_METADATA_NON_NULL(chipAnalysis, false);
     PS_ASSERT_PTR_NON_NULL(view, false);
     PS_ASSERT_PTR_NON_NULL(file, false);
@@ -803,43 +842,17 @@
 
     // read the psf clump data for each region
-    int nRegions = psMetadataLookupS32 (&status, header, "PSF_CLN");
-    if (!status) {
-        // read old-style psf clump data
-
-        char regionName[64];
-        snprintf (regionName, 64, "PSF.CLUMP.REGION.000");
-        psMetadataAddS32 (analysis, PS_LIST_TAIL, "PSF.CLUMP.NREGIONS",  PS_META_REPLACE, "psf clump regions", 1);
-
-        psMetadata *regionMD = psMetadataLookupPtr (&status, analysis, regionName);
-        if (!regionMD) {
-            regionMD = psMetadataAlloc();
-            psMetadataAddMetadata (analysis, PS_LIST_TAIL, regionName, PS_META_REPLACE, "psf clump region", regionMD);
-            psFree (regionMD);
-        }
-
-        // psf clump data
-        pmPSFClump psfClump;
-
-        psfClump.X  = psMetadataLookupF32 (&status, header, "PSF_CLX" );  assert(status);
-        psfClump.Y  = psMetadataLookupF32 (&status, header, "PSF_CLY" );  assert(status);
-        psfClump.dX = psMetadataLookupF32 (&status, header, "PSF_CLDX");  assert(status);
-        psfClump.dY = psMetadataLookupF32 (&status, header, "PSF_CLDY");  assert(status);
-
-        psMetadataAddF32 (regionMD, PS_LIST_TAIL, "PSF.CLUMP.X",  PS_META_REPLACE, "psf clump center", psfClump.X);
-        psMetadataAddF32 (regionMD, PS_LIST_TAIL, "PSF.CLUMP.Y",  PS_META_REPLACE, "psf clump center", psfClump.Y);
-        psMetadataAddF32 (regionMD, PS_LIST_TAIL, "PSF.CLUMP.DX", PS_META_REPLACE, "psf clump center", psfClump.dX);
-        psMetadataAddF32 (regionMD, PS_LIST_TAIL, "PSF.CLUMP.DY", PS_META_REPLACE, "psf clump center", psfClump.dY);
-    } else {
-        psMetadataAddS32 (analysis, PS_LIST_TAIL, "PSF.CLUMP.NREGIONS",  PS_META_REPLACE, "psf clump regions", nRegions);
-
-        for (int i = 0; i < nRegions; i++) {
-            char key[10];
+    if (roAnalysis) {
+        int nRegions = psMetadataLookupS32 (&status, header, "PSF_CLN");
+        if (!status) {
+            // read old-style psf clump data
+
             char regionName[64];
-            snprintf (regionName, 64, "PSF.CLUMP.REGION.%03d", i);
-
-            psMetadata *regionMD = psMetadataLookupPtr (&status, analysis, regionName);
+            snprintf (regionName, 64, "PSF.CLUMP.REGION.000");
+            psMetadataAddS32 (roAnalysis, PS_LIST_TAIL, "PSF.CLUMP.NREGIONS",  PS_META_REPLACE, "psf clump regions", 1);
+
+            psMetadata *regionMD = psMetadataLookupPtr (&status, roAnalysis, regionName);
             if (!regionMD) {
                 regionMD = psMetadataAlloc();
-                psMetadataAddMetadata (analysis, PS_LIST_TAIL, regionName, PS_META_REPLACE, "psf clump region", regionMD);
+                psMetadataAddMetadata (roAnalysis, PS_LIST_TAIL, regionName, PS_META_REPLACE, "psf clump region", regionMD);
                 psFree (regionMD);
             }
@@ -848,12 +861,8 @@
             pmPSFClump psfClump;
 
-            snprintf (key, 9, "CLX_%03d", i);
-            psfClump.X  = psMetadataLookupF32 (&status, header, key);  assert(status);
-            snprintf (key, 9, "CLY_%03d", i);
-            psfClump.Y  = psMetadataLookupF32 (&status, header, key);  assert(status);
-            snprintf (key, 9, "CLDX_%03d", i);
-            psfClump.dX = psMetadataLookupF32 (&status, header, key);  assert(status);
-            snprintf (key, 9, "CLDY_%03d", i);
-            psfClump.dY = psMetadataLookupF32 (&status, header, key);  assert(status);
+            psfClump.X  = psMetadataLookupF32 (&status, header, "PSF_CLX" );  assert(status);
+            psfClump.Y  = psMetadataLookupF32 (&status, header, "PSF_CLY" );  assert(status);
+            psfClump.dX = psMetadataLookupF32 (&status, header, "PSF_CLDX");  assert(status);
+            psfClump.dY = psMetadataLookupF32 (&status, header, "PSF_CLDY");  assert(status);
 
             psMetadataAddF32 (regionMD, PS_LIST_TAIL, "PSF.CLUMP.X",  PS_META_REPLACE, "psf clump center", psfClump.X);
@@ -861,4 +870,36 @@
             psMetadataAddF32 (regionMD, PS_LIST_TAIL, "PSF.CLUMP.DX", PS_META_REPLACE, "psf clump center", psfClump.dX);
             psMetadataAddF32 (regionMD, PS_LIST_TAIL, "PSF.CLUMP.DY", PS_META_REPLACE, "psf clump center", psfClump.dY);
+        } else {
+            psMetadataAddS32 (roAnalysis, PS_LIST_TAIL, "PSF.CLUMP.NREGIONS",  PS_META_REPLACE, "psf clump regions", nRegions);
+
+            for (int i = 0; i < nRegions; i++) {
+                char key[10];
+                char regionName[64];
+                snprintf (regionName, 64, "PSF.CLUMP.REGION.%03d", i);
+
+                psMetadata *regionMD = psMetadataLookupPtr (&status, roAnalysis, regionName);
+                if (!regionMD) {
+                    regionMD = psMetadataAlloc();
+                    psMetadataAddMetadata (roAnalysis, PS_LIST_TAIL, regionName, PS_META_REPLACE, "psf clump region", regionMD);
+                    psFree (regionMD);
+                }
+
+                // psf clump data
+                pmPSFClump psfClump;
+
+                snprintf (key, 9, "CLX_%03d", i);
+                psfClump.X  = psMetadataLookupF32 (&status, header, key);  assert(status);
+                snprintf (key, 9, "CLY_%03d", i);
+                psfClump.Y  = psMetadataLookupF32 (&status, header, key);  assert(status);
+                snprintf (key, 9, "CLDX_%03d", i);
+                psfClump.dX = psMetadataLookupF32 (&status, header, key);  assert(status);
+                snprintf (key, 9, "CLDY_%03d", i);
+                psfClump.dY = psMetadataLookupF32 (&status, header, key);  assert(status);
+
+                psMetadataAddF32 (regionMD, PS_LIST_TAIL, "PSF.CLUMP.X",  PS_META_REPLACE, "psf clump center", psfClump.X);
+                psMetadataAddF32 (regionMD, PS_LIST_TAIL, "PSF.CLUMP.Y",  PS_META_REPLACE, "psf clump center", psfClump.Y);
+                psMetadataAddF32 (regionMD, PS_LIST_TAIL, "PSF.CLUMP.DX", PS_META_REPLACE, "psf clump center", psfClump.dX);
+                psMetadataAddF32 (regionMD, PS_LIST_TAIL, "PSF.CLUMP.DY", PS_META_REPLACE, "psf clump center", psfClump.dY);
+            }
         }
     }
@@ -1019,20 +1060,20 @@
         }
 
-	// note that all N plane are implicitly of the same type, so we convert the mask
-	psImage *mask = psImageCopy(NULL, psf->residuals->mask, psf->residuals->Ro->type.type);
-	psImageInit (psf->residuals->mask, 0);
-	psImageInit (psf->residuals->Rx, 0.0);
-	psImageInit (psf->residuals->Ry, 0.0);
-	switch (Nz) {
-	  case 1: // Ro only
-	    break;
-	  case 2: // Ro and mask
+        // note that all N plane are implicitly of the same type, so we convert the mask
+        psImage *mask = psImageCopy(NULL, psf->residuals->mask, psf->residuals->Ro->type.type);
+        psImageInit (psf->residuals->mask, 0);
+        psImageInit (psf->residuals->Rx, 0.0);
+        psImageInit (psf->residuals->Ry, 0.0);
+        switch (Nz) {
+          case 1: // Ro only
+            break;
+          case 2: // Ro and mask
             if (!psFitsReadImageBuffer(mask, file->fits, fullImage, 1)) {
                 psError(psErrorCodeLast(), false, "Unable to read PSF residual image.");
                 return false;
             }
-	    psImageCopy (psf->residuals->mask, mask, PM_TYPE_RESID_MASK);
-	    break;
-	  case 3: // Ro, Rx and Ry, no mask
+            psImageCopy (psf->residuals->mask, mask, PM_TYPE_RESID_MASK);
+            break;
+          case 3: // Ro, Rx and Ry, no mask
             if (!psFitsReadImageBuffer(psf->residuals->Rx, file->fits, fullImage, 1)) {
                 psError(psErrorCodeLast(), false, "Unable to read PSF residual image.");
@@ -1043,6 +1084,6 @@
                 return false;
             }
-	    break;
-	  case 4: // Ro, Rx, Ry, and mask:
+            break;
+          case 4: // Ro, Rx, Ry, and mask:
             if (!psFitsReadImageBuffer(psf->residuals->Rx, file->fits, fullImage, 1)) {
                 psError(psErrorCodeLast(), false, "Unable to read PSF residual image.");
@@ -1057,11 +1098,11 @@
                 return false;
             }
-	    psImageCopy (psf->residuals->mask, mask, PM_TYPE_RESID_MASK);
-	    break;
-        }
-	psFree (mask);
-    }
-
-    psMetadataAdd (analysis, PS_LIST_TAIL, "PSPHOT.PSF",     PS_DATA_UNKNOWN,  "psphot psf", psf);
+            psImageCopy (psf->residuals->mask, mask, PM_TYPE_RESID_MASK);
+            break;
+        }
+        psFree (mask);
+    }
+
+    psMetadataAdd (chipAnalysis, PS_LIST_TAIL, "PSPHOT.PSF",     PS_DATA_UNKNOWN,  "psphot psf", psf);
     psFree (psf);
 
Index: /branches/pap/psModules/src/objects/pmPSF_IO.h
===================================================================
--- /branches/pap/psModules/src/objects/pmPSF_IO.h	(revision 28483)
+++ /branches/pap/psModules/src/objects/pmPSF_IO.h	(revision 28484)
@@ -20,5 +20,5 @@
 bool pmPSFmodelWriteFPA (pmFPA *fpa, const pmFPAview *view, pmFPAfile *file, pmConfig *config);
 bool pmPSFmodelWriteChip (pmChip *chip, const pmFPAview *view, pmFPAfile *file, pmConfig *config);
-bool pmPSFmodelWrite (psMetadata *analysis, const pmFPAview *view, pmFPAfile *file, pmConfig *config);
+bool pmPSFmodelWrite (const psMetadata *chipAnalysis, const psMetadata *roAnalysis, const pmFPAview *view, pmFPAfile *file, pmConfig *config);
 
 bool pmPSFmodelWritePHU (const pmFPAview *view, pmFPAfile *file, pmConfig *config);
@@ -27,5 +27,5 @@
 bool pmPSFmodelReadFPA (pmFPA *fpa, const pmFPAview *view, pmFPAfile *file, const pmConfig *config);
 bool pmPSFmodelReadChip (pmChip *chip, const pmFPAview *view, pmFPAfile *file, const pmConfig *config);
-bool pmPSFmodelRead (psMetadata *analysis, const pmFPAview *view, pmFPAfile *file, const pmConfig *config);
+bool pmPSFmodelRead (psMetadata *chipAnalysis, psMetadata *roAnalysis, const pmFPAview *view, pmFPAfile *file, const pmConfig *config);
 
 bool pmPSFmodelCheckDataStatusForView (const pmFPAview *view, const pmFPAfile *file);
Index: /branches/pap/psModules/src/objects/pmSourcePhotometry.c
===================================================================
--- /branches/pap/psModules/src/objects/pmSourcePhotometry.c	(revision 28483)
+++ /branches/pap/psModules/src/objects/pmSourcePhotometry.c	(revision 28484)
@@ -107,12 +107,12 @@
         // the source peak pixel is guaranteed to be on the image, and only minimally different from the source center
         double fluxScale = pmTrend2DEval (psf->FluxScale, (float)source->peak->x, (float)source->peak->y);
-	psAssert (isfinite(fluxScale), "how can the flux scale be invalid? source at %d, %d\n", source->peak->x, source->peak->y);
-	psAssert (fluxScale > 0.0, "how can the flux scale be negative? source at %d, %d\n", source->peak->x, source->peak->y);
-	source->psfFlux = fluxScale * source->modelPSF->params->data.F32[PM_PAR_I0];
-	source->psfFluxErr = fluxScale * source->modelPSF->dparams->data.F32[PM_PAR_I0];
-	source->psfMag = -2.5*log10(source->psfFlux);
+        psAssert (isfinite(fluxScale), "how can the flux scale be invalid? source at %d, %d\n", source->peak->x, source->peak->y);
+        psAssert (fluxScale > 0.0, "how can the flux scale be negative? source at %d, %d\n", source->peak->x, source->peak->y);
+        source->psfFlux = fluxScale * source->modelPSF->params->data.F32[PM_PAR_I0];
+        source->psfFluxErr = fluxScale * source->modelPSF->dparams->data.F32[PM_PAR_I0];
+        source->psfMag = -2.5*log10(source->psfFlux);
     } else {
         status = pmSourcePhotometryModel (&source->psfMag, &source->psfFlux, source->modelPSF);
-	source->psfFluxErr = source->psfFlux * (source->modelPSF->dparams->data.F32[PM_PAR_I0] / source->modelPSF->params->data.F32[PM_PAR_I0]);
+        source->psfFluxErr = source->psfFlux * (source->modelPSF->dparams->data.F32[PM_PAR_I0] / source->modelPSF->params->data.F32[PM_PAR_I0]);
     }
 
@@ -120,18 +120,18 @@
     if (source->modelFits) {
         bool foundEXT = false;
-	for (int i = 0; i < source->modelFits->n; i++) {
-	    pmModel *model = source->modelFits->data[i];
-	    status = pmSourcePhotometryModel (&model->mag, NULL, model);
-	    if (model == source->modelEXT) foundEXT = true;
-	}
-	if (foundEXT) {
-	    source->extMag = source->modelEXT->mag;
-	} else {
-	    status = pmSourcePhotometryModel (&source->extMag, NULL, source->modelEXT);
-	}
+        for (int i = 0; i < source->modelFits->n; i++) {
+            pmModel *model = source->modelFits->data[i];
+            status = pmSourcePhotometryModel (&model->mag, NULL, model);
+            if (model == source->modelEXT) foundEXT = true;
+        }
+        if (foundEXT) {
+            source->extMag = source->modelEXT->mag;
+        } else {
+            status = pmSourcePhotometryModel (&source->extMag, NULL, source->modelEXT);
+        }
     } else {
-	if (source->modelEXT) {
-	    status = pmSourcePhotometryModel (&source->extMag, NULL, source->modelEXT);
-	}
+        if (source->modelEXT) {
+            status = pmSourcePhotometryModel (&source->extMag, NULL, source->modelEXT);
+        }
     }
 
@@ -204,5 +204,5 @@
         }
         if (mode & PM_SOURCE_PHOT_APCORR) {
-	    // XXX this should be removed -- we no longer fit for the 'sky bias'
+            // XXX this should be removed -- we no longer fit for the 'sky bias'
             rflux   = pow (10.0, 0.4*source->psfMag);
             source->apMag -= PS_SQR(source->apRadius)*rflux * psf->skyBias + psf->skySat / rflux;
@@ -236,11 +236,11 @@
     flux = model->modelFlux (model->params);
     if (flux > 0) {
-	mag = -2.5*log10(flux);
+        mag = -2.5*log10(flux);
     }
     if (fitMag) {
-	*fitMag = mag;
+        *fitMag = mag;
     }
     if (fitFlux) {
-	*fitFlux = flux;
+        *fitFlux = flux;
     }
 
@@ -380,5 +380,5 @@
 
     if (source->diffStats == NULL) {
-	source->diffStats = pmSourceDiffStatsAlloc();
+        source->diffStats = pmSourceDiffStatsAlloc();
     }
 
@@ -388,5 +388,5 @@
     int   nMask = 0;
     int   nBad  = 0;
-    
+
     psImage *flux     = source->pixels;
     psImage *variance = source->variance;
@@ -394,28 +394,28 @@
 
     for (int iy = 0; iy < flux->numRows; iy++) {
-	for (int ix = 0; ix < flux->numCols; ix++) {
+        for (int ix = 0; ix < flux->numCols; ix++) {
             if (mask->data.PS_TYPE_IMAGE_MASK_DATA[iy][ix] & maskVal) {
-		nMask ++;
-                continue;
-	    }
-
-	    float SN = flux->data.F32[iy][ix] / sqrt(variance->data.F32[iy][ix]);
-
-	    if (SN > +FLUX_LIMIT) { 
-		nGood ++;
-		fGood += fabs(flux->data.F32[iy][ix]);
-	    }
-
-	    if (SN < -FLUX_LIMIT) { 
-		nBad ++;
-		fBad += fabs(flux->data.F32[iy][ix]);
-	    }
-	}
+                nMask ++;
+                continue;
+            }
+
+            float SN = flux->data.F32[iy][ix] / sqrt(variance->data.F32[iy][ix]);
+
+            if (SN > +FLUX_LIMIT) {
+                nGood ++;
+                fGood += fabs(flux->data.F32[iy][ix]);
+            }
+
+            if (SN < -FLUX_LIMIT) {
+                nBad ++;
+                fBad += fabs(flux->data.F32[iy][ix]);
+            }
+        }
     }
 
     source->diffStats->nGood      = nGood;
-    source->diffStats->fRatio     = (fGood + fBad         == 0.0) ? NAN : fGood / (fGood + fBad);	   
-    source->diffStats->nRatioBad  = (nGood + nBad         == 0)   ? NAN : nGood / (float) (nGood + nBad);	   
-    source->diffStats->nRatioMask = (nGood + nMask        == 0)   ? NAN : nGood / (float) (nGood + nMask);	   
+    source->diffStats->fRatio     = (fGood + fBad         == 0.0) ? NAN : fGood / (fGood + fBad);
+    source->diffStats->nRatioBad  = (nGood + nBad         == 0)   ? NAN : nGood / (float) (nGood + nBad);
+    source->diffStats->nRatioMask = (nGood + nMask        == 0)   ? NAN : nGood / (float) (nGood + nMask);
     source->diffStats->nRatioAll  = (nGood + nMask + nBad == 0)   ? NAN : nGood / (float) (nGood + nMask + nBad);
 
@@ -628,5 +628,5 @@
         }
     }
-    model->nPix = Npix; 
+    model->nPix = Npix;
     model->nDOF = Npix - 1;
     model->chisq = dC;
@@ -636,5 +636,5 @@
 
 
-double pmSourceModelWeight(const pmSource *Mi, int term, const bool unweighted_sum, const float covarFactor)
+double pmSourceModelWeight(const pmSource *Mi, int term, const bool unweighted_sum, const float covarFactor, psImageMaskType maskVal)
 {
     PS_ASSERT_PTR_NON_NULL(Mi, NAN);
@@ -652,5 +652,5 @@
     for (int yi = 0; yi < Pi->numRows; yi++) {
         for (int xi = 0; xi < Pi->numCols; xi++) {
-            if (Ti->data.PS_TYPE_IMAGE_MASK_DATA[yi][xi])
+            if (Ti->data.PS_TYPE_IMAGE_MASK_DATA[yi][xi] & maskVal)
                 continue;
             if (!unweighted_sum) {
@@ -684,5 +684,5 @@
 }
 
-double pmSourceModelDotModel (const pmSource *Mi, const pmSource *Mj, const bool unweighted_sum, const float covarFactor)
+double pmSourceModelDotModel (const pmSource *Mi, const pmSource *Mj, const bool unweighted_sum, const float covarFactor, psImageMaskType maskVal)
 {
     PS_ASSERT_PTR_NON_NULL(Mi, NAN);
@@ -727,7 +727,7 @@
     for (yi = yIs, yj = yJs; yi < yIe; yi++, yj++) {
         for (xi = xIs, xj = xJs; xi < xIe; xi++, xj++) {
-            if (Ti->data.PS_TYPE_IMAGE_MASK_DATA[yi][xi])
-                continue;
-            if (Tj->data.PS_TYPE_IMAGE_MASK_DATA[yj][xj])
+            if (Ti->data.PS_TYPE_IMAGE_MASK_DATA[yi][xi] & maskVal)
+                continue;
+            if (Tj->data.PS_TYPE_IMAGE_MASK_DATA[yj][xj] & maskVal)
                 continue;
 
@@ -746,5 +746,5 @@
 }
 
-double pmSourceDataDotModel (const pmSource *Mi, const pmSource *Mj, const bool unweighted_sum, const float covarFactor)
+double pmSourceDataDotModel (const pmSource *Mi, const pmSource *Mj, const bool unweighted_sum, const float covarFactor, psImageMaskType maskVal)
 {
     PS_ASSERT_PTR_NON_NULL(Mi, NAN);
@@ -789,7 +789,7 @@
     for (yi = yIs, yj = yJs; yi < yIe; yi++, yj++) {
         for (xi = xIs, xj = xJs; xi < xIe; xi++, xj++) {
-            if (Ti->data.PS_TYPE_IMAGE_MASK_DATA[yi][xi])
-                continue;
-            if (Tj->data.PS_TYPE_IMAGE_MASK_DATA[yj][xj])
+            if (Ti->data.PS_TYPE_IMAGE_MASK_DATA[yi][xi] & maskVal)
+                continue;
+            if (Tj->data.PS_TYPE_IMAGE_MASK_DATA[yj][xj] & maskVal)
                 continue;
 
Index: /branches/pap/psModules/src/objects/pmSourcePhotometry.h
===================================================================
--- /branches/pap/psModules/src/objects/pmSourcePhotometry.h	(revision 28483)
+++ /branches/pap/psModules/src/objects/pmSourcePhotometry.h	(revision 28484)
@@ -48,5 +48,5 @@
     psImage *image,                     ///< image pixels to be used
     psImage *mask,                      ///< mask of pixels to ignore
-    psImageMaskType maskVal		///< Value to mask
+    psImageMaskType maskVal             ///< Value to mask
 );
 
@@ -58,7 +58,7 @@
 bool pmSourceMeasureDiffStats (pmSource *source, psImageMaskType maskVal);
 
-double pmSourceDataDotModel (const pmSource *Mi, const pmSource *Mj, const bool unweighted_sum, const float covarFactor);
-double pmSourceModelDotModel (const pmSource *Mi, const pmSource *Mj, const bool unweighted_sum, const float covarFactor);
-double pmSourceModelWeight(const pmSource *Mi, int term, const bool unweighted_sum, const float covarFactor);
+double pmSourceDataDotModel (const pmSource *Mi, const pmSource *Mj, const bool unweighted_sum, const float covarFactor, psImageMaskType maskVal);
+double pmSourceModelDotModel (const pmSource *Mi, const pmSource *Mj, const bool unweighted_sum, const float covarFactor, psImageMaskType maskVal);
+double pmSourceModelWeight(const pmSource *Mi, int term, const bool unweighted_sum, const float covarFactor, psImageMaskType maskVal);
 
 // retire these:
Index: /branches/pap/psconfig/psbuild
===================================================================
--- /branches/pap/psconfig/psbuild	(revision 28483)
+++ /branches/pap/psconfig/psbuild	(revision 28484)
@@ -81,5 +81,5 @@
         $developer = 1;
         $optimize = 1;
-	$magic = 1;
+        $magic = 1;
         shift; next;
     }
@@ -183,14 +183,14 @@
     # the operations system needs to first update the magic software
     if ($magic) {
-	$homedir = `pwd`; chomp $homedir;
-	chdir "../magic";
-
-	if (! -d magic || ! -d ssa-core-cpp || ! -d qt-everywhere-opensource-src-4.6.1) {
-	    $status = vsystem ("make update");
-	    if ($status) { die "failed to untar magic directories"; }
-	} else {
-	    print "magic directories exist; run make update manually if needed\n";
-	}
-	chdir $homedir;
+        $homedir = `pwd`; chomp $homedir;
+        chdir "../magic";
+
+        if (! -d "magic" || ! -d "ssa-core-cpp") {
+            $status = vsystem ("make update");
+            if ($status) { die "failed to untar magic directories"; }
+        } else {
+            print "magic directories exist; run make update manually if needed\n";
+        }
+        chdir $homedir;
     }
 
Index: /branches/pap/psconfig/pscheckmods
===================================================================
--- /branches/pap/psconfig/pscheckmods	(revision 28483)
+++ /branches/pap/psconfig/pscheckmods	(revision 28484)
@@ -10,4 +10,5 @@
     exit 1;
 }
+# print "x: $x\n";
 
 $version = eval "\$$ARGV[0]::VERSION";
Index: /branches/pap/psconfig/pscheckperl
===================================================================
--- /branches/pap/psconfig/pscheckperl	(revision 28483)
+++ /branches/pap/psconfig/pscheckperl	(revision 28484)
@@ -147,6 +147,12 @@
         vsystem ("make install");
     }
+    chdir $homedir;
 
-    chdir $homedir;
+    # we claim to have built the module; double-check that it can be found
+    system ("pscheckmods $module $modver");
+    if ($?) {
+        die "failed to find the module we just built: $module\n";
+    }
+    print "built $module\n\n";
 }
 if (!$build) {
Index: /branches/pap/psconfig/psconfig.csh.in
===================================================================
--- /branches/pap/psconfig/psconfig.csh.in	(revision 28483)
+++ /branches/pap/psconfig/psconfig.csh.in	(revision 28484)
@@ -358,4 +358,5 @@
 set plibdir  = {$PSCONFDIR}/{$PSCONFIG}/lib
 set plib5dir = {$PSCONFDIR}/{$PSCONFIG}/lib/perl5
+set plibsite = {$PSCONFDIR}/{$PSCONFIG}/lib/perl5/site_perl
 set newpath = ""
 set pathlist = `echo $PERL5LIB | tr ':' '\n'`
@@ -378,7 +379,7 @@
 else
   if ("$newpath" == "") then
-    setenv PERL5LIB {$plibdir}:{$plib5dir}:
-  else
-    setenv PERL5LIB {$plibdir}:{$plib5dir}:{$newpath}
+    setenv PERL5LIB {$plibdir}:{$plib5dir}:{$plibsite}
+  else
+    setenv PERL5LIB {$plibdir}:{$plib5dir}:{$plibsite}:{$newpath}
   endif 
 endif 
Index: /branches/pap/psconfig/tagsets/ipp-2.9.dist
===================================================================
--- /branches/pap/psconfig/tagsets/ipp-2.9.dist	(revision 28483)
+++ /branches/pap/psconfig/tagsets/ipp-2.9.dist	(revision 28484)
@@ -74,4 +74,5 @@
   YYYYY  ppViz                  ipp-2-9          -0
   YYYYY  ppBackground		ipp-2-9		 -0
+  YYYYY  ppSkycell              ipp-2-9          -0
 
   YYYYY  extsrc/gpcsw           ipp-2-9          -0
Index: /branches/pap/psphot/src/psphotApResid.c
===================================================================
--- /branches/pap/psphot/src/psphotApResid.c	(revision 28483)
+++ /branches/pap/psphot/src/psphotApResid.c	(revision 28484)
@@ -22,9 +22,9 @@
     // loop over the available readouts
     for (int i = 0; i < num; i++) {
-	if (i == chisqNum) continue; // skip chisq image
-	if (!psphotApResidReadout (config, view, filerule, i, recipe)) {
+        if (i == chisqNum) continue; // skip chisq image
+        if (!psphotApResidReadout (config, view, filerule, i, recipe)) {
             psError (PSPHOT_ERR_CONFIG, false, "failed to measure aperture residual for %s entry %d", filerule, i);
-	    return false;
-	}
+            return false;
+        }
     }
     return true;
@@ -56,6 +56,6 @@
 
     if (!sources->n) {
-	psLogMsg ("psphot", PS_LOG_INFO, "no sources, skipping ap resid");
-	return true;
+        psLogMsg ("psphot", PS_LOG_INFO, "no sources, skipping ap resid");
+        return true;
     }
 
@@ -66,5 +66,5 @@
     int nThreads = psMetadataLookupS32(&status, config->arguments, "NTHREADS"); // Number of threads
     if (!status) {
-	nThreads = 0;
+        nThreads = 0;
     }
 
@@ -128,48 +128,46 @@
     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_APRESID_MAGS");
-
-	    psArrayAdd(job->args, 1, cells->data[j]); // sources
-	    psArrayAdd(job->args, 1, psf);
-	    PS_ARRAY_ADD_SCALAR(job->args, photMode, PS_TYPE_S32);
-	    PS_ARRAY_ADD_SCALAR(job->args, maskVal,  PS_TYPE_IMAGE_MASK);
-	    PS_ARRAY_ADD_SCALAR(job->args, markVal,  PS_TYPE_IMAGE_MASK);
-
-	    PS_ARRAY_ADD_SCALAR(job->args, 0,        PS_TYPE_S32); // this is used as a return value for Nskip
-	    PS_ARRAY_ADD_SCALAR(job->args, 0,        PS_TYPE_S32); // this is used as a return value for Nfail
-
-	    if (!psThreadJobAddPending(job)) {
-		psError(PS_ERR_UNKNOWN, false, "Unable to guess model.");
-		psFree (job);
-		return false;
-	    }
-	    psFree(job);
-	}
-
-	// wait for the threads to finish and manage results
-	if (!psThreadPoolWait (false)) {
-	    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");
-	    } else {
-		psScalar *scalar = NULL;
-		scalar = job->args->data[5];
-		Nskip += scalar->data.S32;
-		scalar = job->args->data[6];
-		Nfail += scalar->data.S32;
-	    }
-	    psFree(job);
-	}
+        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_APRESID_MAGS");
+
+            psArrayAdd(job->args, 1, cells->data[j]); // sources
+            psArrayAdd(job->args, 1, psf);
+            PS_ARRAY_ADD_SCALAR(job->args, photMode, PS_TYPE_S32);
+            PS_ARRAY_ADD_SCALAR(job->args, maskVal,  PS_TYPE_IMAGE_MASK);
+            PS_ARRAY_ADD_SCALAR(job->args, markVal,  PS_TYPE_IMAGE_MASK);
+
+            PS_ARRAY_ADD_SCALAR(job->args, 0,        PS_TYPE_S32); // this is used as a return value for Nskip
+            PS_ARRAY_ADD_SCALAR(job->args, 0,        PS_TYPE_S32); // this is used as a return value for Nfail
+
+            if (!psThreadJobAddPending(job)) {
+                psError(PS_ERR_UNKNOWN, false, "Unable to guess model.");
+                return false;
+            }
+        }
+
+        // wait for the threads to finish and manage results
+        if (!psThreadPoolWait (false)) {
+            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");
+            } else {
+                psScalar *scalar = NULL;
+                scalar = job->args->data[5];
+                Nskip += scalar->data.S32;
+                scalar = job->args->data[6];
+                Nfail += scalar->data.S32;
+            }
+            psFree(job);
+        }
     }
 
@@ -184,5 +182,5 @@
     Npsf = 0;
 
-# ifdef DEBUG    
+# ifdef DEBUG
     FILE *f = fopen ("apresid.dat", "w");
     psAssert (f, "failed open");
@@ -199,14 +197,14 @@
         if (source->mode &  PM_SOURCE_MODE_POOR) SKIPSTAR ("POOR STAR");
 
-	if (source->mode &  PM_SOURCE_MODE_EXT_LIMIT) SKIPSTAR ("EXTENDED");
-	if (source->mode &  PM_SOURCE_MODE_CR_LIMIT) SKIPSTAR ("COSMIC RAY");
-	if (source->mode &  PM_SOURCE_MODE_DEFECT) SKIPSTAR ("DEFECT");
-	    
+        if (source->mode &  PM_SOURCE_MODE_EXT_LIMIT) SKIPSTAR ("EXTENDED");
+        if (source->mode &  PM_SOURCE_MODE_CR_LIMIT) SKIPSTAR ("COSMIC RAY");
+        if (source->mode &  PM_SOURCE_MODE_DEFECT) SKIPSTAR ("DEFECT");
+
         if (!isfinite(source->apMag) || !isfinite(source->psfMag)) {
             continue;
         }
 
-	// XXX make this user-configurable?
-	if (source->errMag > 0.01) continue;
+        // XXX make this user-configurable?
+        if (source->errMag > 0.01) continue;
 
         // aperture residual for this source
@@ -221,17 +219,17 @@
 
 # ifdef DEBUG
-	fprintf (f, "%6.1f %6.1f : %6.1f %6.1f : %8.3f %8.3f %8.3f : %f : %f %f %f : %f\n",
-		 source->peak->xf, source->peak->yf, 
-		 source->modelPSF->params->data.F32[PM_PAR_XPOS], source->modelPSF->params->data.F32[PM_PAR_YPOS], 
-		 source->psfMag, source->apMag, source->errMag,
-		 source->modelPSF->params->data.F32[PM_PAR_I0], 
-		 source->modelPSF->params->data.F32[PM_PAR_SXX], source->modelPSF->params->data.F32[PM_PAR_SXY], source->modelPSF->params->data.F32[PM_PAR_SYY], 
-		 source->modelPSF->params->data.F32[PM_PAR_7]);
+        fprintf (f, "%6.1f %6.1f : %6.1f %6.1f : %8.3f %8.3f %8.3f : %f : %f %f %f : %f\n",
+                 source->peak->xf, source->peak->yf,
+                 source->modelPSF->params->data.F32[PM_PAR_XPOS], source->modelPSF->params->data.F32[PM_PAR_YPOS],
+                 source->psfMag, source->apMag, source->errMag,
+                 source->modelPSF->params->data.F32[PM_PAR_I0],
+                 source->modelPSF->params->data.F32[PM_PAR_SXX], source->modelPSF->params->data.F32[PM_PAR_SXY], source->modelPSF->params->data.F32[PM_PAR_SYY],
+                 source->modelPSF->params->data.F32[PM_PAR_7]);
 # endif
-	if (!isfinite(source->psfMag)) psAbort ("nan in psfMag");
-	if (!isfinite(source->errMag)) psAbort ("nan in errMag");
-	if (!isfinite(source->apMag)) psAbort ("nan in apMag");
-	if (!isfinite(model->params->data.F32[PM_PAR_XPOS])) psAbort ("nan in xPos");
-	if (!isfinite(model->params->data.F32[PM_PAR_YPOS])) psAbort ("nan in yPos");
+        if (!isfinite(source->psfMag)) psAbort ("nan in psfMag");
+        if (!isfinite(source->errMag)) psAbort ("nan in errMag");
+        if (!isfinite(source->apMag)) psAbort ("nan in apMag");
+        if (!isfinite(model->params->data.F32[PM_PAR_XPOS])) psAbort ("nan in xPos");
+        if (!isfinite(model->params->data.F32[PM_PAR_YPOS])) psAbort ("nan in yPos");
 
         psVectorAppend (mag, source->psfMag);
@@ -253,9 +251,9 @@
     if (Npsf < APTREND_NSTAR_MIN) {
         psWarning("Only %d valid aperture residual sources (need %d), giving up", Npsf, APTREND_NSTAR_MIN);
-	goto escape;
-    }
-
-    // this is a bit tricky, because we have two cases (MAP vs POLY), and they have a different 
-    // definition for 'order' (order_MAP = order_POLY + 1).  in addition, we have a 
+        goto escape;
+    }
+
+    // this is a bit tricky, because we have two cases (MAP vs POLY), and they have a different
+    // definition for 'order' (order_MAP = order_POLY + 1).  in addition, we have a
     // user-specified MAX order, which we should respect, regardless of the mode
 
@@ -270,6 +268,6 @@
     pmTrend2DMode mode = PM_TREND_MAP;
     if (mode == PM_TREND_MAP) {
-	MaxOrderForStars ++;
-    } 
+        MaxOrderForStars ++;
+    }
     APTREND_ORDER_MAX = PS_MIN (APTREND_ORDER_MAX, MaxOrderForStars);
 
@@ -283,37 +281,37 @@
     int NY = readout->image->numRows;
     for (int i = 1; i <= APTREND_ORDER_MAX; i++) {
-	
-	int Nx, Ny;
-	if (NX > NY) {
-	    Nx = i;
-	    Ny = PS_MAX (1, (int)(i * (NY / (float)(NX)) + 0.5));
-	} else {
-	    Ny = i;
-	    Nx = PS_MAX (1, (int)(i * (NX / (float)(NY)) + 0.5));
-	}
-
-	float errorFloor;
+
+        int Nx, Ny;
+        if (NX > NY) {
+            Nx = i;
+            Ny = PS_MAX (1, (int)(i * (NY / (float)(NX)) + 0.5));
+        } else {
+            Ny = i;
+            Nx = PS_MAX (1, (int)(i * (NX / (float)(NY)) + 0.5));
+        }
+
+        float errorFloor;
         pmTrend2D *apTrend = psphotApResidTrend (&errorFloor, readout, Nx, Ny, xPos, yPos, apResid, dMag);
-	if (!apTrend) {
-	    continue;
-	}
-
-	// apply ApTrend results
-	// float xc = 0.5*readout->image->numCols + readout->image->col0 + 0.5;
-	// float yc = 0.5*readout->image->numRows + readout->image->row0 + 0.5;
-	// float ApResid = pmTrend2DEval (psf->ApTrend, xc, yc); // ap-fit at chip center
-	// if (!isfinite(ApResid)) psAbort("nan apresid @ center");
+        if (!apTrend) {
+            continue;
+        }
+
+        // apply ApTrend results
+        // float xc = 0.5*readout->image->numCols + readout->image->col0 + 0.5;
+        // float yc = 0.5*readout->image->numRows + readout->image->row0 + 0.5;
+        // float ApResid = pmTrend2DEval (psf->ApTrend, xc, yc); // ap-fit at chip center
+        // if (!isfinite(ApResid)) psAbort("nan apresid @ center");
 
         // store the minimum errorFloor and best ApTrend to keep
         if (errorFloor < errorFloorMin) {
             errorFloorMin = errorFloor;
-	    psFree (psf->ApTrend);
-	    psf->ApTrend = psMemIncrRefCounter(apTrend);
-        }
-	psFree (apTrend);
+            psFree (psf->ApTrend);
+            psf->ApTrend = psMemIncrRefCounter(apTrend);
+        }
+        psFree (apTrend);
     }
     if (psf->ApTrend == NULL) {
         psWarning("Failed to find a valid aperture residual value");
-	goto escape;
+        goto escape;
     }
 
@@ -383,11 +381,11 @@
     if (!pmTrend2DFit (apTrend, mask, 0xff, xPos, yPos, apResid, dMagSoft)) {
         psWarning("Failed to fit trend for %d x %d map", Nx, Ny);
-	psFree (apTrend);
-	return NULL;
+        psFree (apTrend);
+        return NULL;
     }
     if (apTrend->mode == PM_TREND_MAP) {
-	// p_psImagePrint (2, apTrend->map->map, "ApTrend Before"); // XXX TEST:
-	psImageMapRepair (apTrend->map->map);
-	// p_psImagePrint (2, apTrend->map->map, "ApTrend After"); // XXX TEST:
+        // p_psImagePrint (2, apTrend->map->map, "ApTrend Before"); // XXX TEST:
+        psImageMapRepair (apTrend->map->map);
+        // p_psImagePrint (2, apTrend->map->map, "ApTrend After"); // XXX TEST:
     }
 
@@ -400,6 +398,6 @@
     if (!isfinite(*apResidSysErr)) {
         psWarning("Failed to find systematic error for %d x %d map", Nx, Ny);
-	psFree (apTrend);
-	return NULL;
+        psFree (apTrend);
+        return NULL;
     }
 
@@ -408,6 +406,6 @@
 
     if (psTraceGetLevel("psphot") >= 4) {
-	char filename[64];
-	snprintf (filename, 64, "apresid.%dx%d.dat", Nx, Ny);
+        char filename[64];
+        snprintf (filename, 64, "apresid.%dx%d.dat", Nx, Ny);
         FILE *dumpFile = fopen (filename, "w");
         for (int i = 0; i < xPos->n; i++) {
@@ -457,28 +455,28 @@
         }
 
-	// clear the mask bit and set the circular mask pixels
-	psImageMaskPixels (source->maskObj, "AND", PS_NOT_IMAGE_MASK(markVal));
-	psImageKeepCircle (source->maskObj, source->peak->x, source->peak->y, source->apRadius, "OR", markVal);
-
-	bool status = pmSourceMagnitudes (source, psf, photMode, maskVal);
-
-	// clear the mask bit 
-	psImageMaskPixels (source->maskObj, "AND", PS_NOT_IMAGE_MASK(markVal));
+        // clear the mask bit and set the circular mask pixels
+        psImageMaskPixels (source->maskObj, "AND", PS_NOT_IMAGE_MASK(markVal));
+        psImageKeepCircle (source->maskObj, source->peak->x, source->peak->y, source->apRadius, "OR", markVal);
+
+        bool status = pmSourceMagnitudes (source, psf, photMode, maskVal);
+
+        // clear the mask bit
+        psImageMaskPixels (source->maskObj, "AND", PS_NOT_IMAGE_MASK(markVal));
 
         // re-subtract the object, leave local sky
         pmSourceSub (source, PM_MODEL_OP_FULL, maskVal);
 
-	if (!status) {
-	    Nskip ++;
-	    psTrace ("psphot", 3, "skip : bad source mag");
-	    continue;
-	}
-    
-	if (!isfinite(source->apMag) || !isfinite(source->psfMag)) {
-	    Nfail ++;
-	    psTrace ("psphot", 3, "fail : nan mags : %f %f", source->apMag, source->psfMag);
-	    continue;
-	}
-	source->mode |= PM_SOURCE_MODE_AP_MAGS;
+        if (!status) {
+            Nskip ++;
+            psTrace ("psphot", 3, "skip : bad source mag");
+            continue;
+        }
+
+        if (!isfinite(source->apMag) || !isfinite(source->psfMag)) {
+            Nfail ++;
+            psTrace ("psphot", 3, "fail : nan mags : %f %f", source->apMag, source->psfMag);
+            continue;
+        }
+        source->mode |= PM_SOURCE_MODE_AP_MAGS;
     }
 
Index: /branches/pap/psphot/src/psphotBlendFit.c
===================================================================
--- /branches/pap/psphot/src/psphotBlendFit.c	(revision 28483)
+++ /branches/pap/psphot/src/psphotBlendFit.c	(revision 28484)
@@ -15,8 +15,8 @@
     // loop over the available readouts
     for (int i = 0; i < num; i++) {
-	if (!psphotBlendFitReadout (config, view, filerule, i, recipe)) {
+        if (!psphotBlendFitReadout (config, view, filerule, i, recipe)) {
             psError (PSPHOT_ERR_CONFIG, false, "failed to fit sources (non-linear) for %s entry %d", filerule, i);
-	    return false;
-	}
+            return false;
+        }
     }
     return true;
@@ -48,6 +48,6 @@
 
     if (!sources->n) {
-	psLogMsg ("psphot", PS_LOG_INFO, "no sources, skipping blend fit");
-	return true;
+        psLogMsg ("psphot", PS_LOG_INFO, "no sources, skipping blend fit");
+        return true;
     }
 
@@ -87,6 +87,6 @@
     sources = psArraySort (sources, pmSourceSortBySN);
     if (!sources->n) {
-	psLogMsg ("psphot", PS_LOG_INFO, "no sources, skipping blend");
-	return true;
+        psLogMsg ("psphot", PS_LOG_INFO, "no sources, skipping blend");
+        return true;
     }
 
@@ -103,26 +103,24 @@
         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_BLEND_FIT");
-	    psArray *newSources = psArrayAllocEmpty(16);
-
-	    psArrayAdd(job->args, 1, readout);
-	    psArrayAdd(job->args, 1, recipe);
-	    psArrayAdd(job->args, 1, cells->data[j]); // sources
-	    psArrayAdd(job->args, 1, psf);
-	    psArrayAdd(job->args, 1, newSources); // return for new sources
-	    psFree (newSources);
-
-	    PS_ARRAY_ADD_SCALAR(job->args, 0, PS_TYPE_S32); // this is used as a return value for Nfit
-	    PS_ARRAY_ADD_SCALAR(job->args, 0, PS_TYPE_S32); // this is used as a return value for Npsf
-	    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 Nfail
-
-	    if (!psThreadJobAddPending(job)) {
-		psError(PS_ERR_UNKNOWN, false, "Unable to guess model.");
-		psFree (job);
-		return NULL;
-	    }
-	    psFree(job);
+            // allocate a job -- if threads are not defined, this just runs the job
+            psThreadJob *job = psThreadJobAlloc ("PSPHOT_BLEND_FIT");
+            psArray *newSources = psArrayAllocEmpty(16);
+
+            psArrayAdd(job->args, 1, readout);
+            psArrayAdd(job->args, 1, recipe);
+            psArrayAdd(job->args, 1, cells->data[j]); // sources
+            psArrayAdd(job->args, 1, psf);
+            psArrayAdd(job->args, 1, newSources); // return for new sources
+            psFree (newSources);
+
+            PS_ARRAY_ADD_SCALAR(job->args, 0, PS_TYPE_S32); // this is used as a return value for Nfit
+            PS_ARRAY_ADD_SCALAR(job->args, 0, PS_TYPE_S32); // this is used as a return value for Npsf
+            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 Nfail
+
+            if (!psThreadJobAddPending(job)) {
+                psError(PS_ERR_UNKNOWN, false, "Unable to guess model.");
+                return NULL;
+            }
 
 # if (0)
@@ -152,33 +150,33 @@
         }
 
-	// wait for the threads to finish and manage results
-	if (!psThreadPoolWait (false)) {
-	    psError(PS_ERR_UNKNOWN, false, "Unable to guess model.");
-	    return NULL;
-	}
-
-	// 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];
-		Nfit += scalar->data.S32;
-		scalar = job->args->data[6];
-		Npsf += scalar->data.S32;
-		scalar = job->args->data[7];
-		Next += scalar->data.S32;
-		scalar = job->args->data[8];
-		Nfail += scalar->data.S32;
-
-		// add these back onto sources
-		psArray *newSources = job->args->data[4];
-		for (int j = 0; j < newSources->n; j++) {
-		    psArrayAdd (sources, 16, newSources->data[j]);
-		}
-	    }
-	    psFree(job);
+        // wait for the threads to finish and manage results
+        if (!psThreadPoolWait (false)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to guess model.");
+            return NULL;
+        }
+
+        // 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];
+                Nfit += scalar->data.S32;
+                scalar = job->args->data[6];
+                Npsf += scalar->data.S32;
+                scalar = job->args->data[7];
+                Next += scalar->data.S32;
+                scalar = job->args->data[8];
+                Nfail += scalar->data.S32;
+
+                // add these back onto sources
+                psArray *newSources = job->args->data[4];
+                for (int j = 0; j < newSources->n; j++) {
+                    psArrayAdd (sources, 16, newSources->data[j]);
+                }
+            }
+            psFree(job);
             }
     }
@@ -278,5 +276,5 @@
                 psTrace ("psphot", 5, "source at %7.1f, %7.1f is ext", source->peak->xf, source->peak->yf);
                 Next ++;
-		source->mode |= PM_SOURCE_MODE_NONLINEAR_FIT;
+                source->mode |= PM_SOURCE_MODE_NONLINEAR_FIT;
                 continue;
             }
@@ -286,5 +284,5 @@
                 psTrace ("psphot", 5, "source at %7.1f, %7.1f is psf", source->peak->xf, source->peak->yf);
                 Npsf ++;
-		source->mode |= PM_SOURCE_MODE_NONLINEAR_FIT;
+                source->mode |= PM_SOURCE_MODE_NONLINEAR_FIT;
                 continue;
             }
Index: /branches/pap/psphot/src/psphotFitSourcesLinear.c
===================================================================
--- /branches/pap/psphot/src/psphotFitSourcesLinear.c	(revision 28483)
+++ /branches/pap/psphot/src/psphotFitSourcesLinear.c	(revision 28484)
@@ -27,24 +27,24 @@
     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?");
-
-	pmPSF *psf = psMetadataLookupPtr (&status, readout->analysis, "PSPHOT.PSF");
-	psAssert (psf, "missing psf?");
-
-	if (!psphotFitSourcesLinearReadout (recipe, readout, sources, psf, final)) {
+        // 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?");
+
+        pmPSF *psf = psMetadataLookupPtr (&status, readout->analysis, "PSPHOT.PSF");
+        psAssert (psf, "missing psf?");
+
+        if (!psphotFitSourcesLinearReadout (recipe, readout, sources, psf, final)) {
             psError (PSPHOT_ERR_CONFIG, false, "failed to fit sources (linear) for %s entry %d", filerule, i);
-	    return false;
-	}
+            return false;
+        }
     }
     return true;
@@ -59,6 +59,6 @@
 
     if (!sources->n) {
-	psLogMsg ("psphot", PS_LOG_INFO, "no sources, skipping linear fit");
-	return true;
+        psLogMsg ("psphot", PS_LOG_INFO, "no sources, skipping linear fit");
+        return true;
     }
 
@@ -150,4 +150,5 @@
 
     if (fitSources->n == 0) {
+        psFree(fitSources);
         return true;
     }
@@ -170,10 +171,10 @@
 
         // diagonal elements of the sparse matrix (auto-cross-product)
-        f = pmSourceModelDotModel (SRCi, SRCi, CONSTANT_PHOTOMETRIC_WEIGHTS, covarFactor);
+        f = pmSourceModelDotModel (SRCi, SRCi, CONSTANT_PHOTOMETRIC_WEIGHTS, covarFactor, maskVal);
         psSparseMatrixElement (sparse, i, i, f);
 
         // the formal error depends on the weighting scheme
         if (CONSTANT_PHOTOMETRIC_WEIGHTS) {
-            float var = pmSourceModelDotModel (SRCi, SRCi, false, covarFactor);
+            float var = pmSourceModelDotModel (SRCi, SRCi, false, covarFactor, maskVal);
             errors->data.F32[i] = 1.0 / sqrt(var);
         } else {
@@ -183,5 +184,5 @@
 
         // find the image x model value
-        f = pmSourceDataDotModel (SRCi, SRCi, CONSTANT_PHOTOMETRIC_WEIGHTS, covarFactor);
+        f = pmSourceDataDotModel (SRCi, SRCi, CONSTANT_PHOTOMETRIC_WEIGHTS, covarFactor, maskVal);
         psSparseVectorElement (sparse, i, f);
 
@@ -189,11 +190,11 @@
         switch (SKY_FIT_ORDER) {
           case 1:
-            f = pmSourceModelWeight (SRCi, 1, CONSTANT_PHOTOMETRIC_WEIGHTS, covarFactor);
+            f = pmSourceModelWeight (SRCi, 1, CONSTANT_PHOTOMETRIC_WEIGHTS, covarFactor, maskVal);
             psSparseBorderElementB (border, i, 1, f);
-            f = pmSourceModelWeight (SRCi, 2, CONSTANT_PHOTOMETRIC_WEIGHTS, covarFactor);
+            f = pmSourceModelWeight (SRCi, 2, CONSTANT_PHOTOMETRIC_WEIGHTS, covarFactor, maskVal);
             psSparseBorderElementB (border, i, 2, f);
 
           case 0:
-            f = pmSourceModelWeight (SRCi, 0, CONSTANT_PHOTOMETRIC_WEIGHTS, covarFactor);
+            f = pmSourceModelWeight (SRCi, 0, CONSTANT_PHOTOMETRIC_WEIGHTS, covarFactor, maskVal);
             psSparseBorderElementB (border, i, 0, f);
             break;
@@ -215,5 +216,5 @@
 
             // got an overlap; calculate cross-product and add to output array
-            f = pmSourceModelDotModel (SRCi, SRCj, CONSTANT_PHOTOMETRIC_WEIGHTS, covarFactor);
+            f = pmSourceModelDotModel (SRCi, SRCj, CONSTANT_PHOTOMETRIC_WEIGHTS, covarFactor, maskVal);
             psSparseMatrixElement (sparse, j, i, f);
         }
Index: /branches/pap/psphot/src/psphotFitSourcesLinearStack.c
===================================================================
--- /branches/pap/psphot/src/psphotFitSourcesLinearStack.c	(revision 28483)
+++ /branches/pap/psphot/src/psphotFitSourcesLinearStack.c	(revision 28484)
@@ -43,25 +43,25 @@
     for (int i = 0; i < objects->n; i++) {
         pmPhotObj *object = objects->data[i];
-	if (!object) continue;
-	if (!object->sources) continue;
+        if (!object) continue;
+        if (!object->sources) continue;
 
-	// XXX check an element of the group to see if we should use it
-	// if (!object->flags & PM_PHOT_OBJ_BAD) continue;
+        // XXX check an element of the group to see if we should use it
+        // if (!object->flags & PM_PHOT_OBJ_BAD) continue;
 
-	for (int j = 0; j < object->sources->n; j++) {
-	  pmSource *source = object->sources->data[j];
-	  if (!source) continue;
+        for (int j = 0; j < object->sources->n; j++) {
+          pmSource *source = object->sources->data[j];
+          if (!source) continue;
 
-	  // turn this bit off and turn it on again if we keep this source
-	  source->mode &= ~PM_SOURCE_MODE_LINEAR_FIT;
+          // turn this bit off and turn it on again if we keep this source
+          source->mode &= ~PM_SOURCE_MODE_LINEAR_FIT;
 
-	  // generate model for sources without, or skip if we can't
-	  if (!source->modelFlux) {
+          // generate model for sources without, or skip if we can't
+          if (!source->modelFlux) {
             if (!pmSourceCacheModel (source, maskVal)) continue;
-	  }
+          }
 
-	  source->mode |= PM_SOURCE_MODE_LINEAR_FIT;
-	  psArrayAdd (fitSources, 100, source);
-	}
+          source->mode |= PM_SOURCE_MODE_LINEAR_FIT;
+          psArrayAdd (fitSources, 100, source);
+        }
     }
     psLogMsg ("psphot.ensemble", PS_LOG_MINUTIA, "built fitSources: %f sec (%ld objects)\n", psTimerMark ("psphot.linear"), objects->n);
@@ -85,10 +85,10 @@
 
         // diagonal elements of the sparse matrix (auto-cross-product)
-        f = pmSourceModelDotModel (SRCi, SRCi, CONSTANT_PHOTOMETRIC_WEIGHTS, COVAR_FACTOR);
+        f = pmSourceModelDotModel (SRCi, SRCi, CONSTANT_PHOTOMETRIC_WEIGHTS, COVAR_FACTOR, maskVal);
         psSparseMatrixElement (sparse, i, i, f);
 
         // the formal error depends on the weighting scheme
         if (CONSTANT_PHOTOMETRIC_WEIGHTS) {
-            float var = pmSourceModelDotModel (SRCi, SRCi, false, COVAR_FACTOR);
+            float var = pmSourceModelDotModel (SRCi, SRCi, false, COVAR_FACTOR, maskVal);
             errors->data.F32[i] = 1.0 / sqrt(var);
         } else {
@@ -97,5 +97,5 @@
 
         // find the image x model value
-        f = pmSourceDataDotModel (SRCi, SRCi, CONSTANT_PHOTOMETRIC_WEIGHTS, COVAR_FACTOR);
+        f = pmSourceDataDotModel (SRCi, SRCi, CONSTANT_PHOTOMETRIC_WEIGHTS, COVAR_FACTOR, maskVal);
         psSparseVectorElement (sparse, i, f);
 
@@ -104,6 +104,6 @@
             pmSource *SRCj = fitSources->data[j];
 
-	    // we only need to generate dot terms for source on the same image
-	    if (SRCj->imageID != SRCi->imageID) { continue; }
+            // we only need to generate dot terms for source on the same image
+            if (SRCj->imageID != SRCi->imageID) { continue; }
 
             // skip over disjoint source images, break after last possible overlap
@@ -114,5 +114,5 @@
 
             // got an overlap; calculate cross-product and add to output array
-            f = pmSourceModelDotModel (SRCi, SRCj, CONSTANT_PHOTOMETRIC_WEIGHTS, COVAR_FACTOR);
+            f = pmSourceModelDotModel (SRCi, SRCj, CONSTANT_PHOTOMETRIC_WEIGHTS, COVAR_FACTOR, maskVal);
             psSparseMatrixElement (sparse, j, i, f);
         }
Index: /branches/pap/psphot/src/psphotGuessModels.c
===================================================================
--- /branches/pap/psphot/src/psphotGuessModels.c	(revision 28483)
+++ /branches/pap/psphot/src/psphotGuessModels.c	(revision 28484)
@@ -21,5 +21,5 @@
     // loop over the available readouts
     for (int i = 0; i < num; i++) {
-	if (i == chisqNum) continue; // skip chisq image
+        if (i == chisqNum) continue; // skip chisq image
         if (!psphotGuessModelsReadout (config, view, filerule, i)) {
             psError (PSPHOT_ERR_CONFIG, false, "failed on to guess models for %s entry %d", filerule, i);
@@ -106,8 +106,6 @@
             if (!psThreadJobAddPending(job)) {
                 psError(PS_ERR_UNKNOWN, false, "Unable to guess model.");
-                psFree (job);
                 return false;
             }
-            psFree(job);
         }
 
Index: /branches/pap/psphot/src/psphotImageLoop.c
===================================================================
--- /branches/pap/psphot/src/psphotImageLoop.c	(revision 28483)
+++ /branches/pap/psphot/src/psphotImageLoop.c	(revision 28484)
@@ -46,4 +46,21 @@
         if (!psphotMosaicChip(config, view, "PSPHOT.INPUT", "PSPHOT.LOAD")) ESCAPE ("Unable to mosaic chip.");
 
+        // Read WCS if easy.
+        // XXX Since we're mosaicking cells, we ignore the case where the WCS is defined for a cell.
+        {
+            pmChip *inChip = pmFPAviewThisChip(view, input->fpa); // Mosaicked chip
+            pmHDU *hduLow = pmHDUGetLowest(input->fpa, inChip, NULL);
+            if (hduLow && !pmAstromReadWCS(input->fpa, inChip, hduLow->header, 1.0)) {
+                psWarning("Unable to read WCS astrometry from header.");
+                psErrorClear();
+                pmHDU *hduHigh = pmHDUGetHighest(input->fpa, inChip, NULL);
+                if (hduHigh && hduHigh != hduLow &&
+                    !pmAstromReadWCS(input->fpa, chip, hduHigh->header, 1.0)) {
+                    psWarning("Unable to read WCS astrometry from primary header.");
+                    psErrorClear();
+                }
+            }
+        }
+
         // try to load other supporting data (PSF, SRC, etc).
         // do not re-load the following three files
@@ -67,19 +84,21 @@
 
                 // Update the header
-		pmHDU *hdu = pmHDUGetHighest(input->fpa, chip, cell);
-		if (hdu && hdu != lastHDU) {
-		    psphotVersionHeaderFull(hdu->header);
-		    lastHDU = hdu;
+                {
+                    pmHDU *hdu = pmHDUGetHighest(input->fpa, chip, cell);
+                    if (hdu && hdu != lastHDU) {
+                        psphotVersionHeaderFull(hdu->header);
+                        lastHDU = hdu;
+                    }
                 }
 
-		// if an external mask is supplied, ensure that NAN pixels are also masked
-		if (readout->mask) {
-		    psImageMaskType maskSat = pmConfigMaskGet("SAT", config); // Mask value for saturated pixels
-		    if (!pmReadoutMaskNonfinite(readout, maskSat)) {
-			psError(psErrorCodeLast(), false, "Unable to mask non-finite pixels.");
-			psFree(view);
-			return false;
-		    }
-		}
+                // if an external mask is supplied, ensure that NAN pixels are also masked
+                if (readout->mask) {
+                    psImageMaskType maskSat = pmConfigMaskGet("SAT", config); // Mask value for saturated pixels
+                    if (!pmReadoutMaskNonfinite(readout, maskSat)) {
+                        psError(psErrorCodeLast(), false, "Unable to mask non-finite pixels.");
+                        psFree(view);
+                        return false;
+                    }
+                }
 
                 // run the actual photometry analysis on this chip/cell/readout
@@ -91,14 +110,14 @@
             }
 
-	    // drop all versions of the internal files
-	    status = true;
-	    status &= pmFPAfileDropInternal (config->files, "PSPHOT.BACKMDL");
-	    status &= pmFPAfileDropInternal (config->files, "PSPHOT.BACKMDL.STDEV");
-	    status &= pmFPAfileDropInternal (config->files, "PSPHOT.BACKGND");
-	    if (!status) {
-		psError(PSPHOT_ERR_PROG, false, "trouble dropping internal files");
-		psFree (view);
-		return false;
-	    }
+            // drop all versions of the internal files
+            status = true;
+            status &= pmFPAfileDropInternal (config->files, "PSPHOT.BACKMDL");
+            status &= pmFPAfileDropInternal (config->files, "PSPHOT.BACKMDL.STDEV");
+            status &= pmFPAfileDropInternal (config->files, "PSPHOT.BACKGND");
+            if (!status) {
+                psError(PSPHOT_ERR_PROG, false, "trouble dropping internal files");
+                psFree (view);
+                return false;
+            }
         }
         // save output which is saved at the chip level
Index: /branches/pap/psphot/src/psphotMagnitudes.c
===================================================================
--- /branches/pap/psphot/src/psphotMagnitudes.c	(revision 28483)
+++ /branches/pap/psphot/src/psphotMagnitudes.c	(revision 28484)
@@ -18,26 +18,26 @@
     // 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->allSources;
-	psAssert (sources, "missing sources?");
-
-	pmPSF *psf = psMetadataLookupPtr (&status, readout->analysis, "PSPHOT.PSF");
-	psAssert (psf, "missing psf?");
-
-	if (!psphotMagnitudesReadout (config, recipe, view, readout, sources, psf)) {
+        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->allSources;
+        psAssert (sources, "missing sources?");
+
+        pmPSF *psf = psMetadataLookupPtr (&status, readout->analysis, "PSPHOT.PSF");
+        psAssert (psf, "missing psf?");
+
+        if (!psphotMagnitudesReadout (config, recipe, view, readout, sources, psf)) {
             psError (PSPHOT_ERR_CONFIG, false, "failed to measure magnitudes for %s entry %d", filerule, i);
-	    return false;
-	}
+            return false;
+        }
     }
     return true;
@@ -50,8 +50,8 @@
 
     if (!sources->n) {
-	psLogMsg ("psphot", PS_LOG_INFO, "no sources, skipping source magnitudes");
-	return true;
-    }
-	
+        psLogMsg ("psphot", PS_LOG_INFO, "no sources, skipping source magnitudes");
+        return true;
+    }
+
     psTimerStart ("psphot.mags");
 
@@ -122,8 +122,6 @@
             if (!psThreadJobAddPending(job)) {
                 psError(PS_ERR_UNKNOWN, false, "Unable to guess model.");
-                psFree (job);
                 return false;
             }
-            psFree(job);
 
 # if (0)
@@ -184,13 +182,13 @@
         }
 
-	// clear the mask bit and set the circular mask pixels
-	psImageMaskPixels (source->maskObj, "AND", PS_NOT_IMAGE_MASK(markVal));
-	psImageKeepCircle (source->maskObj, source->peak->x, source->peak->y, source->apRadius, "OR", markVal);
+        // clear the mask bit and set the circular mask pixels
+        psImageMaskPixels (source->maskObj, "AND", PS_NOT_IMAGE_MASK(markVal));
+        psImageKeepCircle (source->maskObj, source->peak->x, source->peak->y, source->apRadius, "OR", markVal);
 
         status = pmSourceMagnitudes (source, psf, photMode, maskVal); // maskVal includes markVal
         if (status && isfinite(source->apMag)) Nap ++;
 
-	// clear the mask bit 
-	psImageMaskPixels (source->maskObj, "AND", PS_NOT_IMAGE_MASK(markVal));
+        // clear the mask bit
+        psImageMaskPixels (source->maskObj, "AND", PS_NOT_IMAGE_MASK(markVal));
 
         // re-subtract the object, leave local sky
@@ -273,9 +271,6 @@
             if (!psThreadJobAddPending(job)) {
                 psError(PS_ERR_UNKNOWN, false, "Unable to guess model.");
-                psFree (job);
                 return false;
             }
-            psFree(job);
-
         }
 
Index: /branches/pap/psphot/src/psphotRadiusChecks.c
===================================================================
--- /branches/pap/psphot/src/psphotRadiusChecks.c	(revision 28483)
+++ /branches/pap/psphot/src/psphotRadiusChecks.c	(revision 28484)
@@ -4,7 +4,7 @@
 static float PSF_FIT_NSIGMA;
 static float PSF_FIT_PADDING;
-static float PSF_APERTURE = 0;	// radius to use in PSF aperture mags
-static float PSF_FIT_RADIUS = 0;	// radius to use in fitting (ignored if <= 0,
-					// and a per-object radius is calculated)
+static float PSF_APERTURE = 0;  // radius to use in PSF aperture mags
+static float PSF_FIT_RADIUS = 0;        // radius to use in fitting (ignored if <= 0,
+                                        // and a per-object radius is calculated)
 
 bool psphotInitRadiusPSF(const psMetadata *recipe, const psMetadata *analysis, const pmModelType type) {
@@ -17,10 +17,30 @@
     PSF_FIT_RADIUS =  psMetadataLookupF32(&status, analysis, "PSF_FIT_RADIUS");
     if (!status) {
-	PSF_FIT_RADIUS = psMetadataLookupF32(&status, recipe, "PSF_FIT_RADIUS");
+        PSF_FIT_RADIUS = psMetadataLookupF32(&status, recipe, "PSF_FIT_RADIUS");
     }
 
     PSF_APERTURE =  psMetadataLookupF32(&status, analysis, "PSF_APERTURE");
     if (!status) {
-	PSF_APERTURE =  psMetadataLookupF32(&status, recipe, "PSF_APERTURE");
+        PSF_APERTURE =  psMetadataLookupF32(&status, recipe, "PSF_APERTURE");
+    }
+
+    // The PSF_FIT_RADIUS and PSF_APERTURE may not be set if the PSF was loaded and not chosen
+
+    if (PSF_FIT_RADIUS == 0.0) {
+        float gaussSigma = psMetadataLookupF32(&status, analysis, "MOMENTS_GAUSS_SIGMA");
+        if (!status) {
+            gaussSigma = psMetadataLookupF32(&status, recipe, "MOMENTS_GAUSS_SIGMA");
+        }
+        float fitScale = psMetadataLookupF32(&status, recipe, "PSF_FIT_RADIUS_SCALE");
+        PSF_FIT_RADIUS = (int)(fitScale*gaussSigma);
+    }
+
+    if (PSF_APERTURE == 0.0) {
+        float gaussSigma = psMetadataLookupF32(&status, analysis, "MOMENTS_GAUSS_SIGMA");
+        if (!status) {
+            gaussSigma = psMetadataLookupF32(&status, recipe, "MOMENTS_GAUSS_SIGMA");
+        }
+        float apScale = psMetadataLookupF32(&status, recipe, "PSF_APERTURE_SCALE");
+        PSF_APERTURE = (int)(apScale*gaussSigma);
     }
 
@@ -38,18 +58,18 @@
     // set the fit radius based on the object flux limit and the model
     float radiusFit = PSF_FIT_RADIUS;
-    if (radiusFit <= 0) {		// use fixed radius
-	if (moments == NULL) {
-	    radiusFit = model->modelRadius(model->params, PSF_FIT_NSIGMA*moments->dSky);
-	} else {
-	    radiusFit = model->modelRadius(model->params, 1.0);
-	}
-	model->fitRadius = (RADIUS_TYPE)(radiusFit + PSF_FIT_PADDING);
+    if (radiusFit <= 0) {               // use fixed radius
+        if (moments == NULL) {
+            radiusFit = model->modelRadius(model->params, PSF_FIT_NSIGMA*moments->dSky);
+        } else {
+            radiusFit = model->modelRadius(model->params, 1.0);
+        }
+        model->fitRadius = (RADIUS_TYPE)(radiusFit + PSF_FIT_PADDING);
     } else {
-	model->fitRadius = radiusFit;
+        model->fitRadius = radiusFit;
     }
     if (isnan(model->fitRadius)) psAbort("error in radius");
-	
+
     if (source->mode & PM_SOURCE_MODE_SATSTAR) {
-	model->fitRadius *= 2;
+        model->fitRadius *= 2;
     }
 
@@ -73,13 +93,13 @@
     // set the fit radius based on the object flux limit and the model
     float radiusFit = PSF_FIT_RADIUS;
-    if (radiusFit <= 0) {		// use fixed radius
-	if (moments == NULL) {
-	    radiusFit = model->modelRadius(model->params, PSF_FIT_NSIGMA*moments->dSky);
-	} else {
-	    radiusFit = model->modelRadius(model->params, 1.0);
-	}
-	model->fitRadius = (RADIUS_TYPE)(radiusFit + PSF_FIT_PADDING);
+    if (radiusFit <= 0) {               // use fixed radius
+        if (moments == NULL) {
+            radiusFit = model->modelRadius(model->params, PSF_FIT_NSIGMA*moments->dSky);
+        } else {
+            radiusFit = model->modelRadius(model->params, 1.0);
+        }
+        model->fitRadius = (RADIUS_TYPE)(radiusFit + PSF_FIT_PADDING);
     } else {
-	model->fitRadius = radiusFit;
+        model->fitRadius = radiusFit;
     }
     if (isnan(model->fitRadius)) psAbort("error in radius");
@@ -89,5 +109,5 @@
 
     if (source->mode &  PM_SOURCE_MODE_SATSTAR) {
-	model->fitRadius *= 2;
+        model->fitRadius *= 2;
     }
 
@@ -134,12 +154,12 @@
     float radius = 0.0;
     for (int j = 0; j < footprint->spans->n; j++) {
-	pmSpan *span = footprint->spans->data[j];
-
-	float dY  = span->y  - peak->yf;
-	float dX0 = span->x0 - peak->xf;
-	float dX1 = span->x1 - peak->xf;
-
-	radius = PS_MAX (radius, hypot(dY, dX0));
-	radius = PS_MAX (radius, hypot(dY, dX1));
+        pmSpan *span = footprint->spans->data[j];
+
+        float dY  = span->y  - peak->yf;
+        float dX0 = span->x0 - peak->xf;
+        float dX1 = span->x1 - peak->xf;
+
+        radius = PS_MAX (radius, hypot(dY, dX0));
+        radius = PS_MAX (radius, hypot(dY, dX1));
     }
 
Index: /branches/pap/psphot/src/psphotReadout.c
===================================================================
--- /branches/pap/psphot/src/psphotReadout.c	(revision 28483)
+++ /branches/pap/psphot/src/psphotReadout.c	(revision 28484)
@@ -45,4 +45,5 @@
         return psphotReadoutCleanup (config, view, "PSPHOT.INPUT");
     }
+
     if (!psphotSubtractBackground (config, view, "PSPHOT.INPUT")) {
         return psphotReadoutCleanup (config, view, "PSPHOT.INPUT");
@@ -93,4 +94,5 @@
         return psphotReadoutCleanup (config, view, "PSPHOT.INPUT");
     }
+
     // if we were not supplied a PSF model, determine the IQ stats here (detections->newSources)
     if (!psphotImageQuality (config, view, "PSPHOT.INPUT")) { // pass 1
@@ -208,6 +210,8 @@
 
     // calculate source magnitudes
-    psphotMagnitudes(config, view, "PSPHOT.INPUT"); // pass 1 (detections->allSources)
-
+    if (!psphotMagnitudes(config, view, "PSPHOT.INPUT")) { // pass 1 (detections->allSources)
+      psErrorStackPrint(stderr, "Unable to do magnitudes.");
+        psErrorClear();
+    }
     if (!psphotEfficiency(config, view, "PSPHOT.INPUT")) { // pass 1
         psErrorStackPrint(stderr, "Unable to determine detection efficiencies from fake sources");
@@ -219,9 +223,19 @@
 
     // replace background in residual image
-    psphotSkyReplace (config, view, "PSPHOT.INPUT"); // pass 1
-
+    if (!psphotSkyReplace (config, view, "PSPHOT.INPUT")) { // pass 1
+      psErrorStackPrint(stderr, "Unable to replace sky");
+      psErrorClear();
+
+/*       psLogMsg("psphot", 3, "failed on psphotSkyReplace"); */
+/*       return(psphotReadoutCleanup(config, view, "PSPHOT.INPUT")); */
+    }
     // drop the references to the image pixels held by each source
-    psphotSourceFreePixels (config, view, "PSPHOT.INPUT"); // pass 1
-
+    if (!psphotSourceFreePixels (config, view, "PSPHOT.INPUT")) { // pass 1
+      psErrorStackPrint(stderr, "Unable to free source pixels");
+      psErrorClear();
+
+/*       psLogMsg ("psphot", 3, "failed on psphotSourceFreePixels"); */
+/*       return(psphotReadoutCleanup(config, view, "PSPHOT.INPUT")); */
+    }
     // create the exported-metadata and free local data
     return psphotReadoutCleanup(config, view, "PSPHOT.INPUT");
Index: /branches/pap/psphot/src/psphotRoughClass.c
===================================================================
--- /branches/pap/psphot/src/psphotRoughClass.c	(revision 28483)
+++ /branches/pap/psphot/src/psphotRoughClass.c	(revision 28484)
@@ -25,9 +25,9 @@
     // loop over the available readouts
     for (int i = 0; i < num; i++) {
-	if (i == chisqNum) continue; // skip chisq image
-	if (!psphotRoughClassReadout (config, view, filerule, i, recipe)) {
+        if (i == chisqNum) continue; // skip chisq image
+        if (!psphotRoughClassReadout (config, view, filerule, i, recipe)) {
             psError (PSPHOT_ERR_CONFIG, false, "failed on rough classification for %s entry %d", filerule, i);
-	    return false;
-	}
+            return false;
+        }
     }
     return true;
@@ -50,5 +50,5 @@
     bool havePSF = false;
     if (psMetadataLookupPtr (&status, readout->analysis, "PSPHOT.PSF")) {
-	havePSF = true;
+        havePSF = true;
     }
 
@@ -60,6 +60,6 @@
 
     if (!sources->n) {
-	psLogMsg ("psphot", PS_LOG_INFO, "no sources, skipping rough classification");
-	return true;
+        psLogMsg ("psphot", PS_LOG_INFO, "no sources, skipping rough classification");
+        return true;
     }
 
@@ -78,4 +78,17 @@
                 psLogMsg ("psphot", 4, "Failed to determine rough classification for region %f,%f - %f,%f\n",
                          region->x0, region->y0, region->x1, region->y1);
+
+                // If in doubt, it's a PSF
+                for (int i = 0; i < sources->n; i++) {
+                    pmSource *source = sources->data[i]; // Source of interest
+                    if (!source || !source->peak) {
+                        continue;
+                    }
+                    if (source->peak->x <  region->x0) continue;
+                    if (source->peak->x >= region->x1) continue;
+                    if (source->peak->y <  region->y0) continue;
+                    if (source->peak->y >= region->y1) continue;
+                    source->type = PM_SOURCE_TYPE_STAR;
+                }
                 psFree (region);
                 continue;
@@ -124,22 +137,22 @@
         // XXX why not save the psfClump as a PTR?
 
-	float PSF_SN_LIM = psMetadataLookupF32(&status, recipe, "PSF_SN_LIM"); psAssert (status, "missing PSF_SN_LIM");
-	float MOMENTS_AR_MAX = psMetadataLookupF32(&status, recipe, "MOMENTS_AR_MAX"); psAssert (status, "missing MOMENTS_AR_MAX");
+        float PSF_SN_LIM = psMetadataLookupF32(&status, recipe, "PSF_SN_LIM"); psAssert (status, "missing PSF_SN_LIM");
+        float MOMENTS_AR_MAX = psMetadataLookupF32(&status, recipe, "MOMENTS_AR_MAX"); psAssert (status, "missing MOMENTS_AR_MAX");
 
-	float PSF_CLUMP_GRID_SCALE = psMetadataLookupF32(&status, analysis, "PSF_CLUMP_GRID_SCALE");
-	if (!status) {
-	    PSF_CLUMP_GRID_SCALE = psMetadataLookupF32(&status, recipe, "PSF_CLUMP_GRID_SCALE");
-	    psAssert (status, "missing PSF_CLUMP_GRID_SCALE");
-	}
-	float MOMENTS_SX_MAX = psMetadataLookupF32(&status, analysis, "MOMENTS_SX_MAX");
-	if (!status) {
-	    MOMENTS_SX_MAX = psMetadataLookupF32(&status, recipe, "MOMENTS_SX_MAX");
-	    psAssert (status, "missing MOMENTS_SX_MAX");
-	}
-	float MOMENTS_SY_MAX = psMetadataLookupF32(&status, analysis, "MOMENTS_SY_MAX");
-	if (!status) {
-	    MOMENTS_SY_MAX = psMetadataLookupF32(&status, recipe, "MOMENTS_SY_MAX");
-	    psAssert (status, "missing MOMENTS_SY_MAX");
-	}
+        float PSF_CLUMP_GRID_SCALE = psMetadataLookupF32(&status, analysis, "PSF_CLUMP_GRID_SCALE");
+        if (!status) {
+            PSF_CLUMP_GRID_SCALE = psMetadataLookupF32(&status, recipe, "PSF_CLUMP_GRID_SCALE");
+            psAssert (status, "missing PSF_CLUMP_GRID_SCALE");
+        }
+        float MOMENTS_SX_MAX = psMetadataLookupF32(&status, analysis, "MOMENTS_SX_MAX");
+        if (!status) {
+            MOMENTS_SX_MAX = psMetadataLookupF32(&status, recipe, "MOMENTS_SX_MAX");
+            psAssert (status, "missing MOMENTS_SX_MAX");
+        }
+        float MOMENTS_SY_MAX = psMetadataLookupF32(&status, analysis, "MOMENTS_SY_MAX");
+        if (!status) {
+            MOMENTS_SY_MAX = psMetadataLookupF32(&status, recipe, "MOMENTS_SY_MAX");
+            psAssert (status, "missing MOMENTS_SY_MAX");
+        }
 
         psfClump = pmSourcePSFClump (NULL, region, sources, PSF_SN_LIM, PSF_CLUMP_GRID_SCALE, MOMENTS_SX_MAX, MOMENTS_SY_MAX, MOMENTS_AR_MAX);
Index: /branches/pap/psphot/src/psphotSourceStats.c
===================================================================
--- /branches/pap/psphot/src/psphotSourceStats.c	(revision 28483)
+++ /branches/pap/psphot/src/psphotSourceStats.c	(revision 28484)
@@ -3,5 +3,5 @@
 // convert detections to sources and measure their basic properties (moments, local sky, sky
 // variance) Note: this function only generates sources for the new peaks (peak->assigned).
-// The new sources are added to any existing sources on detections->newSources.  The sources 
+// The new sources are added to any existing sources on detections->newSources.  The sources
 // on detections->allSources are ignored.
 bool psphotSourceStats (pmConfig *config, const pmFPAview *view, const char *filerule, bool setWindow)
@@ -22,5 +22,5 @@
     // loop over the available readouts
     for (int i = 0; i < num; i++) {
-	// if (i == chisqNum) continue; // skip chisq image
+        // if (i == chisqNum) continue; // skip chisq image
         if (!psphotSourceStatsReadout (config, view, filerule, i, recipe, setWindow)) {
             psError (PSPHOT_ERR_CONFIG, false, "failed to find initial detections for %s entry %d", filerule, i);
@@ -91,5 +91,5 @@
     // generate the array of sources, define the associated pixel
     if (!detections->newSources) {
-	detections->newSources = psArrayAllocEmpty (peaks->n);
+        detections->newSources = psArrayAllocEmpty (peaks->n);
     }
     sources = detections->newSources;
@@ -107,5 +107,5 @@
         // create a new source
         pmSource *source = pmSourceAlloc();
-	source->imageID = index;
+        source->imageID = index;
 
         // add the peak
@@ -184,9 +184,7 @@
             if (!psThreadJobAddPending(job)) {
                 psError(PS_ERR_UNKNOWN, false, "Unable to launch thread job PSPHOT_SOURCE_STATS");
-                psFree (job);
                 psFree(detections->newSources);
                 return false;
             }
-            psFree(job);
         }
 
@@ -194,5 +192,5 @@
         if (!psThreadPoolWait (false)) {
             psError(PS_ERR_UNKNOWN, false, "Failure in thread job PSPHOT_SOURCE_STATS");
-	    psFree(detections->newSources);
+            psFree(detections->newSources);
             return false;
         }
@@ -315,8 +313,6 @@
             if (!psThreadJobAddPending(job)) {
                 psError(PS_ERR_UNKNOWN, false, "Unable to launch thread job PSPHOT_SOURCE_STATS");
-                psFree (job);
                 return NULL;
             }
-            psFree(job);
         }
 
@@ -380,6 +376,6 @@
         pmSource *source = sources->data[i];
 
-	if (source->tmpFlags & PM_SOURCE_TMPF_MOMENTS_MEASURED) continue;
-	source->tmpFlags |= PM_SOURCE_TMPF_MOMENTS_MEASURED;
+        if (source->tmpFlags & PM_SOURCE_TMPF_MOMENTS_MEASURED) continue;
+        source->tmpFlags |= PM_SOURCE_TMPF_MOMENTS_MEASURED;
 
         // skip faint sources for moments measurement
@@ -493,4 +489,6 @@
         psLogMsg ("psphot", 3, "radius %.1f, nStars: %d, nSigma: %5.2f, X,  Y: %f, %f (%f, %f)\n", sigma[i], psfClump.nStars, psfClump.nSigma, psfClump.X, psfClump.Y, sqrt(psfClump.X) / sigma[i], sqrt(psfClump.Y) / sigma[i]);
 
+#if 0
+        // Modifying clump parameters without restoring!
         psMetadataAddS32 (analysis, PS_LIST_TAIL, "PSF.CLUMP.NREGIONS",  PS_META_REPLACE, "psf clump regions", 1);
         psMetadata *regionMD = psMetadataLookupPtr (&status, analysis, "PSF.CLUMP.REGION.000");
@@ -504,6 +502,6 @@
         psMetadataAddF32 (regionMD, PS_LIST_TAIL, "PSF.CLUMP.DX", PS_META_REPLACE, "psf clump center", psfClump.dX);
         psMetadataAddF32 (regionMD, PS_LIST_TAIL, "PSF.CLUMP.DY", PS_META_REPLACE, "psf clump center", psfClump.dY);
-
         psphotVisualPlotMoments (recipe, analysis, sources);
+#endif
 
         Sout[i] = sqrt(0.5*(psfClump.X + psfClump.Y)) / sigma[i];
Index: /branches/pap/psphot/src/psphotTest.c
===================================================================
--- /branches/pap/psphot/src/psphotTest.c	(revision 28483)
+++ /branches/pap/psphot/src/psphotTest.c	(revision 28484)
@@ -25,5 +25,5 @@
 
 bool FillImage_Threaded (psThreadJob *job) {
-    
+
     psImage *image = job->args->data[0];
     int xs = PS_SCALAR_VALUE(job->args->data[1],S32);
@@ -36,7 +36,7 @@
     psRegion region = psRegionSet (xs, xs + dx, ys, ys + dy);
     for (int i = 0; i < 100; i++) {
-	psImage *subset = psImageSubset (image, region);
-	psImageInit (subset, value + i);
-	psFree (subset);
+        psImage *subset = psImageSubset (image, region);
+        psImageInit (subset, value + i);
+        psFree (subset);
     }
     return true;
@@ -46,6 +46,6 @@
 
     if (argc != 3) {
-	fprintf (stderr, "USAGE: psphotTest (output.fits) (nThreads)\n");
-	exit (2);
+        fprintf (stderr, "USAGE: psphotTest (output.fits) (nThreads)\n");
+        exit (2);
     }
 
@@ -62,25 +62,23 @@
 
     for (int ix = 0; ix < 1000; ix += 100) {
-	for (int iy = 0; iy < 1000; iy += 100) {
+        for (int iy = 0; iy < 1000; iy += 100) {
 
-	    // allocate a job -- if threads are not defined, this just runs the job
-	    psThreadJob *job = psThreadJobAlloc ("FILL_IMAGE");
+            // allocate a job -- if threads are not defined, this just runs the job
+            psThreadJob *job = psThreadJobAlloc ("FILL_IMAGE");
 
-	    psArrayAdd(job->args, 1, image);
-	    PS_ARRAY_ADD_SCALAR(job->args, ix, PS_TYPE_S32);
-	    PS_ARRAY_ADD_SCALAR(job->args, iy, PS_TYPE_S32);
-	    PS_ARRAY_ADD_SCALAR(job->args, 100, PS_TYPE_S32);
-	    PS_ARRAY_ADD_SCALAR(job->args, 100, PS_TYPE_S32);
-	    PS_ARRAY_ADD_SCALAR(job->args, ix + iy, PS_TYPE_S32);
+            psArrayAdd(job->args, 1, image);
+            PS_ARRAY_ADD_SCALAR(job->args, ix, PS_TYPE_S32);
+            PS_ARRAY_ADD_SCALAR(job->args, iy, PS_TYPE_S32);
+            PS_ARRAY_ADD_SCALAR(job->args, 100, PS_TYPE_S32);
+            PS_ARRAY_ADD_SCALAR(job->args, 100, PS_TYPE_S32);
+            PS_ARRAY_ADD_SCALAR(job->args, ix + iy, PS_TYPE_S32);
 
-	    // FillImage (image, ix, iy, 100, 100, ix + iy);
+            // FillImage (image, ix, iy, 100, 100, ix + iy);
 
-	    if (!psThreadJobAddPending(job)) {
-		fprintf (stderr, "failure to run FillImage(1)");
-		psFree (job);
-		exit (1);
-	    }
-	    psFree(job);
-	}
+            if (!psThreadJobAddPending(job)) {
+                fprintf (stderr, "failure to run FillImage(1)");
+                exit (1);
+            }
+        }
     }
 
@@ -88,6 +86,6 @@
     // wait for the threads to finish and manage results
     if (!psThreadPoolWait (true)) {
-	fprintf (stderr, "failure to run FillImage (2)");
-	exit (1);
+        fprintf (stderr, "failure to run FillImage (2)");
+        exit (1);
     }
 
Index: /branches/pap/pstamp/scripts/detectability_respond.pl
===================================================================
--- /branches/pap/pstamp/scripts/detectability_respond.pl	(revision 28483)
+++ /branches/pap/pstamp/scripts/detectability_respond.pl	(revision 28484)
@@ -226,4 +226,5 @@
 my @psphot_Qfact= ();
 my @psphot_flux = ();
+my @psphot_error = ();
 
 foreach my $k (keys %image_list_hash) {
@@ -231,5 +232,5 @@
 	my $cmf = "$image_list_hash{$k}{OUTROOT}.$image_list_hash{$k}{CLASS_ID}.cmf";
 	
-	my ($tmp_Npix,$tmp_Qfact,$tmp_flux) = read_cmf_file($cmf,$image_list_hash{$k}{EXTENSION_BASE});
+	my ($tmp_Npix,$tmp_Qfact,$tmp_flux,$tmp_flux_error) = read_cmf_file($cmf,$image_list_hash{$k}{EXTENSION_BASE});
 	
 	push @rownums,        @{ $image_list_hash{$k}{ROWNUM} };
@@ -238,4 +239,5 @@
 	push @psphot_Qfact,   @{ $tmp_Qfact };
 	push @psphot_flux,    @{ $tmp_flux };
+	push @psphot_error,   @{ $tmp_flux_error };
     }
     else {
@@ -245,4 +247,5 @@
 	push @psphot_Qfact,   (map { 0.0 }  @{ $image_list_hash{$k}{ROWNUM} });
 	push @psphot_flux,    (map { 0.0 }  @{ $image_list_hash{$k}{ROWNUM} });
+	push @psphot_error,    (map { 0.0 }  @{ $image_list_hash{$k}{ROWNUM} });
     }	
 }
@@ -252,5 +255,5 @@
 		    $query{HEADER}{MJD_OBS}[0],$query{HEADER}{filter}[0],
 		    $query{HEADER}{obscode}[0],
-		    \@rownums, \@out_errors, \@psphot_Npix, \@psphot_Qfact, \@psphot_flux);
+		    \@rownums, \@out_errors, \@psphot_Npix, \@psphot_Qfact, \@psphot_flux, \@psphot_error);
 # print "Wrote response file $output\n";
 #
@@ -406,4 +409,5 @@
     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);
@@ -428,4 +432,5 @@
 	    { name => 'PSF_NPIX', type => '1J', writetype => TLONG },
 	    { name => 'PSF_INST_MAG', type => '1E', writetype => TDOUBLE },
+	    { name => 'PSF_INST_MAG_SIG', type => '1E', writetype => TDOUBLE },
 	    ];
     }
@@ -435,4 +440,5 @@
 	    { name => 'PSF_NPIX', type => '1J', writetype => TLONG },
 	    { name => 'PSF_INST_FLUX', type => '1E', writetype => TDOUBLE },
+	    { name => 'PSF_INST_FLUX_SIG', type => '1E', writetype => TDOUBLE },
 	    ];
     }
@@ -450,4 +456,5 @@
     $inFits->get_num_rows($numRows, $status) and check_fitsio($status);
 
+    my $correct_error = 0;
     foreach my $col (@$column_defs) {
 	my ($col_num,$col_type,$col_data);
@@ -469,10 +476,22 @@
 	    @tmp_flux = map { $_ = 10**(-0.4 * $_) } @{ $col_data };
 	}
+	elsif ($col->{name} eq 'PSF_INST_MAG_SIG') {
+	    @tmp_flux_err = map { $_ = $_ / (2.5 * log10(exp(1))) } @{ $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);    
-    return(\@tmp_Npix, \@tmp_Qfact, \@tmp_flux);
+    return(\@tmp_Npix, \@tmp_Qfact, \@tmp_flux, \@tmp_flux_err);
 }
 
@@ -490,4 +509,5 @@
     my $psphot_Qfact_ref = shift;
     my $psphot_flux_ref = shift;
+    my $psphot_error_ref = shift;
     my $status = 0;
 
@@ -504,4 +524,6 @@
 	# 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 },
 	];
     
@@ -543,6 +565,10 @@
 	push @{$colData{'DETECT_F'}},    ${ $psphot_Qfact_ref }[$i];
 	push @{$colData{'TARGET_FLUX'}}, ${ $psphot_flux_ref }[$i];
+	push @{$colData{'TARGET_FLUX_SIG'}}, ${ $psphot_error_ref }[$i];
 	unless (defined(${ $colData{'TARGET_FLUX'}}[-1])) {
 	    $colData{'TARGET_FLUX'}[-1] = 0.0;
+	}
+	unless (defined(${ $colData{'TARGET_FLUX_SIG'}}[-1])) {
+	    $colData{'TARGET_FLUX_SIG'}[-1] = 0.0;
 	}
     }
Index: /branches/pap/pstamp/scripts/pstamp_finish.pl
===================================================================
--- /branches/pap/pstamp/scripts/pstamp_finish.pl	(revision 28483)
+++ /branches/pap/pstamp/scripts/pstamp_finish.pl	(revision 28484)
@@ -76,5 +76,5 @@
 if ($product eq "NULL") {
     # nothing more to do
-    stop_request_and_exit($req_id, $PS_EXIT_PROG_ERROR);
+    my_die("product is NULL!", $req_id, $PS_EXIT_PROG_ERROR);
 }
 
@@ -92,19 +92,14 @@
 
         if (!mkdir $outdir) {
-            print STDERR "cannot create output directory $outdir";
-            stop_request_and_exit($req_id, $PS_EXIT_UNKNOWN_ERROR);
+            my_die("cannot create output directory $outdir",$req_id, $PS_EXIT_UNKNOWN_ERROR);
         }
 
 
     } elsif (! -d $outdir ) {
-        # XXX TODO: fault the request so we pstamp_finish doesn't keep trying to process the
-        # request
-        print STDERR "output directory $outdir exists but is not a directory";
-        stop_request_and_exit($req_id, $PS_EXIT_UNKNOWN_ERROR);
+        my_die("output directory $outdir exists but is not a directory", $req_id, $PS_EXIT_UNKNOWN_ERROR);
     }
 
     if (! -e $req_file ) {
-        print STDERR "request file $req_file is missing\n";
-        stop_request_and_exit($req_id, $PS_EXIT_CONFIG_ERROR);
+        my_die("request file $req_file is missing", $req_id, $PS_EXIT_CONFIG_ERROR);
     }
 
@@ -115,6 +110,5 @@
         # Since a request got queued, the request file must have been readable at some
         # point 
-        print STDERR "failed to read request_file $req_file" ;
-        stop_request_and_exit($req_id, $PS_EXIT_CONFIG_ERROR);
+        my_die("failed to read request file $req_file", $req_id, $PS_EXIT_CONFIG_ERROR);
     }
 
@@ -151,15 +145,7 @@
         my $output = join "", @$stdout_buf;
         if (!$output) {
-            if ($verbose) {
-                print STDERR "Request $req_id produced no jobs.\n"
-            }
             # This should not happen. A fake job should have been entered
-            stop_request_and_exit($req_id, $PS_EXIT_PROG_ERROR);
+            my_die("Request $req_id produced no jobs", $req_id, $PS_EXIT_PROG_ERROR);
         } else {
-if (0) {
-            my $metadata = $mdcParser->parse($output) or die("Unable to parse metdata config doc");
-
-            my $jobs = parse_md_list($metadata);
-}
             my $jobs = parse_md_fast($mdcParser, $output);
 
@@ -181,5 +167,5 @@
             # this request had a duplicate request name yet the parser didn't give
             # it an "ERROR style name.
-            stop_request_and_exit($req_id, $PS_EXIT_PROG_ERROR);
+            my_die("duplicate request not given a request name", $req_id, $PS_EXIT_PROG_ERROR);
         }
         my ($row, $req_info, $project) = get_request_info($rows, $rownum);
@@ -187,9 +173,7 @@
         my $proj_hash = resolve_project($ipprc, $project, $dbname, $dbserver);
         my $image_db = $proj_hash->{dbname};
-        if (!$image_db) {
-            carp("failed to find imagedb for project: $project");
-            if (!$fault) {
-                stop_request_and_exit($req_id, $PS_EXIT_CONFIG_ERROR);
-            }
+        if (!$image_db and !$fault) {
+            # if project isn't resolvable, the paser should have faulted this job
+            my_die("failed to find imagedb for project $project", $req_id, $PS_EXIT_CONFIG_ERROR);
         }
 
@@ -273,6 +257,6 @@
                 run(command => $command, verbose => $verbose);
             unless ($success) {
-                print STDERR "Unable to perform $command error code: $error_code\n";
                 $request_fault = $error_code >> 8;
+                my_die("Unable to perform $command error code: $error_code", $req_id, $request_fault);
             }
         }
@@ -282,19 +266,15 @@
         my $command = "$dsreg --list $reglist_name --add $fileset --product $product --type PSRESULTS";
         $command .= " --link --datapath $outdir --ps0 $req_id";
-# XXX: let dsreg and config handle resolving dbname and dbserver
-#        $command .= " --dbname $dbname" if $dbname;
 
         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");
-            print STDERR "Unable to perform $command error code: $error_code\n";
             $request_fault = $error_code >> 8;
-            # fall through to stop the request
+            my_die("Unable to perform $command error code: $error_code\n", $req_id, $request_fault);
         }
     }
     # set the request's state to stop
     {
-        my $command = "$pstamptool -updatereq -req_id $req_id -set_state stop -set_fault $request_fault";
+        my $command = "$pstamptool -updatereq -req_id $req_id -set_state stop";
         $command   .= " -dbname $dbname" if $dbname;
         $command   .= " -dbserver $dbserver" if $dbserver;
@@ -308,9 +288,12 @@
 }
 
-sub stop_request_and_exit {
+sub my_die {
+    my $msg = shift;
     my $req_id = shift;
     my $fault  = shift;
 
-    my $command = "$pstamptool -updatereq -req_id $req_id -set_state stop -set_fault $fault";
+    carp($msg);
+
+    my $command = "$pstamptool -updatereq -req_id $req_id -set_fault $fault";
     $command   .= " -dbname $dbname" if $dbname;
     $command   .= " -dbserver $dbserver" if $dbserver;
@@ -445,9 +428,4 @@
                 return undef;
             }
-if (0) {
-            my $metadata = $mdcParser->parse($output) or die("Unable to parse metdata config doc");
-
-            my $exposures = parse_md_list($metadata);
-}
             my $exposures = parse_md_fast($mdcParser, $output);
             my $numExp = @$exposures;
Index: /branches/pap/pstamp/scripts/pstamp_parser_run.pl
===================================================================
--- /branches/pap/pstamp/scripts/pstamp_parser_run.pl	(revision 28483)
+++ /branches/pap/pstamp/scripts/pstamp_parser_run.pl	(revision 28484)
@@ -198,5 +198,5 @@
 $parse_cmd .= " --verbose" if $verbose;
 
-my $newState = "run";
+my $newState;
 my $fault;
 {
@@ -224,7 +224,8 @@
     }
 
-    unless ($success) {
+    if ($success) {
+        $newState = 'run';
+    } else {
         $fault = $error_code >> 8;
-#        $newState = "stop";
     }
 }
@@ -234,5 +235,6 @@
 #
 {
-    my $command = "$pstamptool -updatereq -req_id $req_id -set_state $newState";
+    my $command = "$pstamptool -updatereq -req_id $req_id";
+    $command   .= " -set_state $newState" if $newState;
     $command   .= " -set_outdir $outdir";
     $command   .= " -set_reqType $reqType" if $reqType;
Index: /branches/pap/pstamp/scripts/pstampparse.pl
===================================================================
--- /branches/pap/pstamp/scripts/pstampparse.pl	(revision 28483)
+++ /branches/pap/pstamp/scripts/pstampparse.pl	(revision 28484)
@@ -361,5 +361,4 @@
     my $proj_hash = resolve_project($ipprc, $project, $dbname, $dbserver);
     if (!$proj_hash) {
-        print STDERR "project $project not found\n" ;
         foreach $row (@$rowList) {
             insertFakeJobForRow($row, 1, $PSTAMP_UNKNOWN_PRODUCT);
Index: /branches/pap/pstamp/src/ppstampMakeStamp.c
===================================================================
--- /branches/pap/pstamp/src/ppstampMakeStamp.c	(revision 28483)
+++ /branches/pap/pstamp/src/ppstampMakeStamp.c	(revision 28484)
@@ -688,5 +688,10 @@
         }
     }
-    psU32 maskMask = ~convPoor;
+    psU32 suspect = psMetadataLookupU32(&status, masks, "SUSPECT");
+    if (!status) {
+        psError(PM_ERR_CONFIG, false, "failed to lookup mask value for SUSPECT in recipes\n");
+        return false;
+    }
+    psU32 maskMask = ~(convPoor | suspect);
 
     double exciseValue;
Index: /branches/pap/pstamp/src/pstamp.h
===================================================================
--- /branches/pap/pstamp/src/pstamp.h	(revision 28483)
+++ /branches/pap/pstamp/src/pstamp.h	(revision 28484)
@@ -33,6 +33,8 @@
 #define PSTAMP_SELECT_BACKMDL   32
 #define PSTAMP_SELECT_INVERSE 1024
+#define PSTAMP_SELECT_UNCONV  2048
+#define PSTAMP_USE_IMFILE_ID 16384
 
-#define PSTAMP_WAIT_FOR_UPDATE 2048
+#define PSTAMP_NO_WAIT_FOR_UPDATE 32768
 
 #define PSTAMP_CENTER_IN_PIXELS 1
Index: /branches/pap/pswarp/src/pswarpParseCamera.c
===================================================================
--- /branches/pap/pswarp/src/pswarpParseCamera.c	(revision 28483)
+++ /branches/pap/pswarp/src/pswarpParseCamera.c	(revision 28484)
@@ -61,4 +61,5 @@
 {
     psAssert(config, "Require configuration");
+    bool mdok;                          // Status of MD lookup
 
     // The input image(s) is required: it defines the camera
@@ -94,4 +95,10 @@
     psFree(skyConfig);
 
+    psMetadata *recipe = psMetadataLookupPtr (&status, config->recipes, PSWARP_RECIPE);
+    if (!recipe) {
+        psError(PSWARP_ERR_CONFIG, false, "missing recipe %s", PSWARP_RECIPE);
+        return false;
+    }
+
     // The output skycell
     pmFPAfile *output = pmFPAfileDefineSkycell(config, NULL, "PSWARP.OUTPUT");
@@ -118,5 +125,5 @@
     }
 
-    if (astrom) {
+    if (astrom && psMetadataLookupBool(&mdok, recipe, "SOURCES")) {
         pmFPAfile *outSources = pmFPAfileDefineSkycell(config, output->fpa, "PSWARP.OUTPUT.SOURCES");
         if (!outSources) {
@@ -127,11 +134,4 @@
     }
 
-    psMetadata *recipe = psMetadataLookupPtr (&status, config->recipes, PSWARP_RECIPE);
-    if (!recipe) {
-        psError(PSWARP_ERR_CONFIG, false, "missing recipe %s", PSWARP_RECIPE);
-        return false;
-    }
-
-    bool mdok;                          // Status of MD lookup
     if (psMetadataLookupBool(&mdok, recipe, "PSF")) {
         // This file, PSPHOT.INPUT, is just used as a carrier; output files (eg, PSPHOT.RESID) are defined by
Index: /branches/pap/pswarp/src/pswarpTransformReadout.c
===================================================================
--- /branches/pap/pswarp/src/pswarpTransformReadout.c	(revision 28483)
+++ /branches/pap/pswarp/src/pswarpTransformReadout.c	(revision 28484)
@@ -132,5 +132,4 @@
                 return false;
             }
-            psFree(job);
             psFree(args);
         }
@@ -196,5 +195,5 @@
     psFree(interp);
 
-    if (goodPixels > 0) {
+    if (goodPixels > 0 && psMetadataLookupBool(&mdok, recipe, "SOURCES")) {
         if (!pswarpTransformSources(output, input, config)) {
             psError(psErrorCodeLast(), false, "Unable to interpolate image.");
Index: /branches/pap/tools/chip_outputs.pl
===================================================================
--- /branches/pap/tools/chip_outputs.pl	(revision 28483)
+++ /branches/pap/tools/chip_outputs.pl	(revision 28484)
@@ -23,4 +23,6 @@
                              'PSF' => '.psf',     # Point-spread function
                              'LOG' => '.log',     # Processing log
+                             'PATTERN' => '.ptn', # Pattern subtraction
+                             'MODEL' => '.mdl.fits', # Background model
                          };
 
Index: /branches/pap/tools/czartool.pl
===================================================================
--- /branches/pap/tools/czartool.pl	(revision 28483)
+++ /branches/pap/tools/czartool.pl	(revision 28484)
@@ -18,4 +18,5 @@
 my @stdscienceLabels = `czartool_getLabels.pl -s stdscience`;
 my @distributionLabels = `czartool_getLabels.pl -s distribution`;
+my @publishingLabels = `czartool_getLabels.pl -s publishing`;
 
 checkAllLabels("new");
@@ -215,28 +216,34 @@
     my ($state) = @_;
 #print time, $/;
-    printf("\n+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n");
-    printf("|                                                                           %10s (any faults are shown in parentheses)                                                  |\n", $state);
-    printf("+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n");
-    printf("| no  |              label               | distributing? |    chip    |    cam     |    fake    |    warp    |   stack    |    diff    |   magic    |  destreak  |    dist    |\n");
-    printf("+-----+----------------------------------+---------------+------------+------------+------------+------------+------------+------------+------------+------------+------------+\n");
+    printf("\n+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n");
+    printf("|                                                                                  %10s (any faults are shown in parentheses)                                                           |\n", $state);
+    printf("+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\n");
+    printf("| no  |              label               | distributing? |  publishing?  |    chip    |    cam     |    fake    |    warp    |   stack    |    diff    |   magic    |  destreak  |    dist    |\n");
+    printf("+-----+----------------------------------+---------------+---------------+------------+------------+------------+------------+------------+------------+------------+------------+------------+\n");
 
     my $stdsLabel;
     my $distLabel;
+    my $pubLabel;
     my $i=1;
     my $distributing;
+    my $publishing;
     foreach $stdsLabel (@stdscienceLabels) {
 
-    $distributing = 0;
+        $distributing = 0;
+        chomp($stdsLabel);
         foreach $distLabel (@distributionLabels) {
-            chomp($stdsLabel);
             chomp($distLabel);
             if ($stdsLabel eq $distLabel) { $distributing = 1; last;}
-
-
         }
-        chomp($stdsLabel);
+
+        foreach $pubLabel (@publishingLabels) {
+            chomp($pubLabel);
+            if ($stdsLabel eq $pubLabel) { $publishing = 1; last;}
+        }
+
         printf("| %3d ", $i);
         printf("| %32s ", $stdsLabel);
         printf("| %10s    ", $distributing ? "yes" : "NO" );
+        printf("| %10s    ", $publishing ? "yes" : "NO" );
         printf("| %10s ", getStateAndFaults($stdsLabel, "chipRun", $state, "chip"));
         printf("| %10s ", getStateAndFaults($stdsLabel, "camRun", $state, "cam"));
@@ -252,5 +259,5 @@
     }
 
-    printf("+-----+----------------------------------+---------------+------------+------------+------------+------------+------------+------------+------------+------------+------------+\n");
+    printf("+-----+----------------------------------+---------------+---------------+------------+------------+------------+------------+------------+------------+------------+------------+------------+\n");
 
 }
Index: /branches/pap/tools/diff_outputs.pl
===================================================================
--- /branches/pap/tools/diff_outputs.pl	(revision 28483)
+++ /branches/pap/tools/diff_outputs.pl	(revision 28484)
@@ -15,16 +15,17 @@
 # List of products
 use constant PRODUCTS => { 'REGULAR' => [ 'IMAGE', 'MASK', 'VARIANCE',
-                                          'SOURCES', 'JPEG1', 'JPEG2',
-                                          'KERNEL', 'LOG' ],
-                           'ALL' => [ 'IMAGE', 'MASK', 'VARIANCE', 'SOURCES', 'JPEG1', 'JPEG2', 'KERNEL',
+                                          'SOURCES', 'PSF', 'JPEG1', 'JPEG2',
+                                          'KERNEL', 'LOG', 'STATS', ],
+                           'ALL' => [ 'IMAGE', 'MASK', 'VARIANCE',
+                                      'SOURCES', 'PSF', 'JPEG1', 'JPEG2', 'KERNEL',
                                       'INVERSE.IMAGE', 'INVERSE.MASK', 'INVERSE.VARIANCE', 'INVERSE.SOURCES',
 #                                      'INCONV.IMAGE', 'INCONV.MASK', 'INCONV.VARIANCE',
 #                                      'REFCONV.IMAGE', 'REFCONV.MASK', 'REFCONV.VARIANCE',
-                                      'LOG',
+                                      'LOG', 'STATS',
                                ],
                            'INVERSE' => [ 'INVERSE.IMAGE', 'INVERSE.MASK',
                                           'INVERSE.VARIANCE',
                                           'INVERSE.SOURCES',
-                                          'LOG', ],
+                                          'LOG', 'STATS',],
                        };
 
@@ -34,4 +35,5 @@
                              'VARIANCE' => '.wt.fits', # Variance
                              'SOURCES' => '.cmf', # Sources
+                             'PSF' => '.psf',     # PSF
                              'JPEG1' => '.b1.jpg', # Binned JPEG
                              'JPEG2' => '.b2.jpg', # Binned JPEG
@@ -48,4 +50,5 @@
                              'REFCONV.VARIANCE' => '.refConv.wt.fits', # Convolved reference variance
                              'LOG' => '.log', # Log file
+                             'STATS' => '.stats', # Statistics file
                          };
 
Index: /branches/pap/tools/errors.pl
===================================================================
--- /branches/pap/tools/errors.pl	(revision 28483)
+++ /branches/pap/tools/errors.pl	(revision 28484)
@@ -78,4 +78,12 @@
     $label_table = "magicDSRun";
     $fault_table = "magicDSFile";
+} elsif ($stage eq "publish") {
+    $sql = "SELECT pub_id, client_id, hostname, path_base FROM publishRun JOIN publishDone USING(pub_id) WHERE fault != 0 AND state = 'new'";
+    $label_table = "publishRun";
+    $fault_table = "publishDone";
+} elsif ($stage eq "diffphot") {
+    $sql = "SELECT diff_phot_id, skycell_id, hostname, path_base FROM diffPhotRun JOIN diffPhotSkyfile USING(diff_phot_id) WHERE fault != 0 AND state = 'new'";
+    $label_table = "diffPhotRun";
+    $fault_table = "diffPhotSkycell";
 } else {
     die "Unsupported stage: $stage\n";
Index: /branches/pap/tools/psstatus
===================================================================
--- /branches/pap/tools/psstatus	(revision 28484)
+++ /branches/pap/tools/psstatus	(revision 28484)
@@ -0,0 +1,145 @@
+#!/bin/env perl
+
+use strict;
+use warnings;
+
+use DBI;
+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::Temp qw(tempfile);
+
+my $limit = 20;
+my $running;
+my $req_faulted;
+my $job_faulted;
+my $dbname;
+my $dbserver;
+my $dbuser;
+my $dbpassword;
+my $use_mysql = 1;
+my $verbose;
+
+GetOptions(
+    'running|r',     \$running,
+    'req_faulted',   \$req_faulted,
+    'job_faulted',   \$job_faulted,
+    'limit|l=i',     \$limit,
+    'dbname=s',      \$dbname,
+    'verbose|v',     \$verbose,
+) or pod2usage (2);
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+
+my $no_args = ! (defined $running or defined $req_faulted or defined $job_faulted );
+
+my $ipprc =  PS::IPP::Config->new();
+my $dbh = getDBHandle();
+
+my $select = "SELECT req_id, name, label, reqType, pstampRequest.state, pstampRequest.fault, count(job_id) AS numJobs, timestamp";
+
+$select .= ", outdir" if ($verbose);
+my $from = " FROM pstampRequest LEFT JOIN pstampJob USING(req_id)";
+my $where = " WHERE (pstampRequest.state NOT like '%cleaned')";
+
+my $sql = $select . $from . $where;
+
+if ($no_args and $limit) {
+    # use mysql to get the last_req_id
+    # this works but it prints out the @last_req_id value which is distracting. Use dbi for the query
+    my $stmt = $dbh->prepare("SELECT req_id FROM pstampRequest ORDER BY req_id DESC LIMIT 1");
+    $stmt->execute();
+    my $row = $stmt->fetchrow_hashref();
+    die "failed to get last req_id\n"  if !$row;
+    my $last_req_id = $row->{req_id};
+    die "failed to get last req_id\n"  if !$last_req_id;
+
+    my $first_req_id_to_select = $last_req_id - $limit + 1;
+    $sql .= " AND (req_id >= $first_req_id_to_select)";
+}
+if ($running) {
+    $sql .= " AND (pstampRequest.state = 'new' OR pstampRequest.state = 'run')"
+}
+if ($req_faulted) {
+    $sql .= " AND (pstampRequest.fault > 0)";
+}
+if ($job_faulted) {
+    $sql .= " AND (pstampJob.fault > 0 AND pstampJob.fault < 10)";
+}
+$sql .= " GROUP BY req_id";
+
+if ($limit and !$no_args) {
+    $sql .= " LIMIT $limit";
+}
+
+if ($use_mysql) {
+#    print STDERR "$sql\n";
+    my @args = ( "--table", "--user=$dbuser",  "--host=$dbserver", "--password=$dbpassword", $dbname);
+
+    push @args, '--verbose' if $verbose;
+
+    open my $mysql, "|-", 'mysql', @args or die "failed to run mysql\n";
+    print $mysql "$sql;\n";
+    close $mysql;
+
+} else {
+        # I thought that I wanted custom formatting but in the end I figured I'd just use mysql
+
+        my $stmt = $dbh->prepare($sql);
+        $stmt->execute();
+
+        my $head = "| req_id | name                                                            | label           | reqType   | state | fault  |  numJobs   | outdir";
+        my $head2= "-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------";
+        #           |   5499 | cclin_20100522T011813                                           | WEB.UP          | pstamp    | stop  |      0 |          1 |/data/ippdb02.0/pstamp/work/20100521/5517"
+
+        my $printhead = 1;
+        my $count = 0;
+        while (  my $row = $stmt->fetchrow_hashref() ) {
+            $count++;
+            if ($printhead) {
+                print "$head\n";
+                print "$head2\n";
+                $printhead = 0;
+            }
+            printf "|%7d | %-64s| %-16s| %-10s| %-6s| %6d | %10d | %-s |\n",
+                $row->{req_id}, be_safe($row->{name}), $row->{label}, be_safe($row->{reqType}), $row->{state}, $row->{fault},
+                $row->{numJobs}, $row->{outdir};
+        }
+
+        if (!$count) {
+            print "no requests found\n";
+        } else {
+            print "$head2\n";
+        }
+
+}
+
+exit 0;
+
+sub be_safe {
+    my $str = shift;
+
+    return $str ? $str : 'null';
+}
+
+
+sub getDBHandle {
+    $dbserver = metadataLookupStr($ipprc->{_siteConfig}, "PS_DBSERVER");
+    $dbuser = metadataLookupStr($ipprc->{_siteConfig}, "DS_DBUSER");
+    $dbpassword = metadataLookupStr($ipprc->{_siteConfig}, "DS_DBPASSWORD");
+    if (!$dbname) {
+        $dbname = metadataLookupStr($ipprc->{_siteConfig}, "PS_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;
+}
+
+
