Index: /branches/cnb_branches/cnb_branch_20090301/Nebulous-Server/MANIFEST
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Nebulous-Server/MANIFEST	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/Nebulous-Server/MANIFEST	(revision 23352)
@@ -10,10 +10,8 @@
 bin/neb-voladd
 bin/nebdiskd
-docs/c_api.h
 docs/database_setup.txt
 docs/design.txt
+docs/install.txt
 docs/requirements.txt
-docs/setup.txt
-docs/tmp.txt
 examples/Makefile
 examples/nebexample.c
Index: /branches/cnb_branches/cnb_branch_20090301/Nebulous-Server/bin/neb-admin
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Nebulous-Server/bin/neb-admin	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/Nebulous-Server/bin/neb-admin	(revision 23352)
@@ -23,4 +23,6 @@
     $dbpass,
     $dbuser,
+    $so_id_start,
+    $so_id_range,
     $limit,
     $pending,
@@ -41,4 +43,6 @@
     'pendingreplicate|r'    => \$pending,
     'pendingremoval'        => \$removal,
+    'so_id_start=i'         => \$so_id_start,
+    'so_id_range=i'         => \$so_id_range,
     'limit|l=i'             => \$limit,
     'verbose|v'             => \$verbose,
@@ -58,4 +62,5 @@
 
 # check to make sure that only one instance of neb-admin is running
+# XXX this implies we should move pending replicate elsewhere
 my $pidfile = '/var/tmp/neb-admin';
 # abort if an instance is already running
@@ -91,4 +96,22 @@
     $dbh->do("INSERT INTO myvolume SELECT * FROM volume");
 
+    if (not defined $so_id_start) { $so_id_start = 0; }
+    if (not defined $so_id_range) { $so_id_range = 100000; }
+    my $so_id_end = $so_id_start + $so_id_range;
+
+    # XXX check if so_id_start is beyond MAX(so_id): if so, exit with exit status 10
+    { 
+	my $query = $dbh->prepare("SELECT MAX(so_id) from storage_object");
+	$query->execute;
+        my $answer = $query->fetchrow_arrayref;
+        my $max_so_id = $$answer[0];
+	$query->finish;
+
+	if ($so_id_start > $max_so_id) { 
+	    print STDERR "at end of so_id range, reset please\n";
+	    exit 10;
+	}
+    }
+	
     my $query = $dbh->prepare(
     "        SELECT
@@ -111,5 +134,7 @@
                 USING(vol_id)
             WHERE mymountedvol.available = 1
-    --        WHERE storage_object_xattr.name = 'user.copies'
+            AND storage_object_xattr.name = 'user.copies'
+            AND storage_object.so_id >= $so_id_start
+            AND storage_object.so_id <  $so_id_end
             GROUP BY so_id
             HAVING available_instances < instances OR instances < copies
Index: /branches/cnb_branches/cnb_branch_20090301/Nebulous-Server/bin/neb-voladd
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Nebulous-Server/bin/neb-voladd	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/Nebulous-Server/bin/neb-voladd	(revision 23352)
@@ -79,5 +79,5 @@
 =head1 SYNOPSIS
 
-    neb-voladd --volume <volume name> --uri <volume uri>
+    neb-voladd --vname <volume name> --vhost <host name> --uri <volume uri>
     [--db <database>] [--user <username>] [--pass <password>] [--host <hostname]
 
@@ -92,7 +92,11 @@
 =over 4
 
-=item * --volume <volume name>
+=item * --vname <volume name>
 
 Symbolic name of volume being added.
+
+=item * --vhost <host name>
+
+Name of the host that containes the C<URI> of the volume being added. 
 
 =item * --uri|-u <volume uri>
Index: anches/cnb_branches/cnb_branch_20090301/Nebulous-Server/docs/c_api.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Nebulous-Server/docs/c_api.h	(revision 23351)
+++ 	(revision )
@@ -1,24 +1,0 @@
-/*
- * Copyright (C) 2004 Joshua Hoblitt
- *
- * $Id: c_api.h,v 1.1.1.1 2004-12-22 02:16:23 jhoblitt Exp $
- */
-
-// should there be an argument for "suggested" storage location?
-int add( const char *ext_id, const char *data, int size )
-
-int delete( const char *ext_id )
-
-// should there be an argument for "suggested" storage location?
-int update( const char *ext_id, const char *data, int size )
-
-// should there be an argument for "suggested" storage location?
-int move( const char *old_ext_id, const char *new_ext_id )
-
-// open returns a fd 
-int open( const char *ext_id )
-
-int close( const char *ext_id )
-
-// maybe open and close are a bad idea, perhaps...
-int read( const char *ext_id, char *data )
Index: /branches/cnb_branches/cnb_branch_20090301/Nebulous-Server/docs/install.txt
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Nebulous-Server/docs/install.txt	(revision 23352)
+++ /branches/cnb_branches/cnb_branch_20090301/Nebulous-Server/docs/install.txt	(revision 23352)
@@ -0,0 +1,373 @@
+=pod
+
+=head1 Installing a Nebulous Server
+
+=head2 Create a UNIX group for Nebulous
+
+All Nebulous user's need to be in the same UNIX group as the Nebulous server.
+
+    groupadd -g 877 nebulous
+
+This is a silly hack to generate a gid.
+
+    perl -le 'map { $gid += ord } split //, shift; print $gid' nebulous
+
+=head2 Add the users that you want to be able to access Nebulous to that group
+
+    usermod -G nebulous foouser
+
+=head2 Setup directory permissions for the nebulous group
+
+As root:
+
+    mkdir /data/ipp000.0/nebulous
+    mkdir /data/ipp000.1/nebulous
+    mkdir /data/ipp002.0/nebulous
+    mkdir /data/ipp003.0/nebulous
+
+    # note that this is just an example, this won't work over NFS with
+    # root_squash enablded
+    chown nobody:nebulous /data/ipp00?.?/nebulous
+
+    chmod 0770 /data/ipp00?.?/nebulous
+    ls -lad /data/ipp00?.?/nebulous
+
+    mkdir /data/`hostname`.0/nebulous
+    chown nobody:nebulous /data/`hostname`.0/nebulous
+    chmod 0770 /data/`hostname`.0/nebulous
+
+=head2 Build and install the Nebulous Perl modules (see: README)
+
+=head2 Create and initialize the database
+
+    mysql -u root mysql -p
+    (enter your MySQL root password)
+
+    DROP DATABASE IF EXISTS nebulous;
+    CREATE DATABASE nebulous;
+    GRANT ALL PRIVILEGES ON nebulous.* TO 'nebulous'@'localhost'
+        IDENTIFIED BY '@neb@';
+    FLUSH PRIVILEGES;
+    exit
+
+    export NEB_DB=nebulous
+    export NEB_USER=nebulous
+    export NEB_PASS=@neb@
+
+    neb-initdb
+    neb-addvol --name ipp000.0 --uri file:///data/ipp000.0/nebulous
+    neb-addvol --name ipp000.1 --uri file:///data/ipp000.1/nebulous
+    neb-addvol --name ipp002.0 --uri file:///data/ipp002.0/nebulous
+    neb-addvol --name ipp003.0 --uri file:///data/ipp003.0/nebulous
+
+#    perl -e 'for (1..24) { $i = sprintf "po%02d", $_ ; system "neb-addvol --name $i --uri file:/$i/nebulous" }'
+    .
+    .
+
+If you get an error similar to this while initializing the database it means
+that the creation of the Nebulous's stored procedures failed:
+
+    ERROR 1146 (42S02): Table 'mysql.proc' doesn't exist
+
+It probably means that your instance of MySQL has been upgraded for an older
+version that did not support stored precedures.  You can fix this by running
+the C<mysql_fix_privilege_tables> script that you should have received with
+MySQL.
+
+In order for the test suite to pass the "test" account needs have the
+appropriate permissions to create and modify stored procedures.
+
+    update user set Create_routine_priv = 'Y', Alter_routine_priv = 'Y', Execute_priv = 'Y' where user = 'test';
+    update db set Create_routine_priv = 'Y', Alter_routine_priv = 'Y', Execute_priv = 'Y' where db = 'test';
+
+    flush privileges;
+
+Please see this webpage for further details:
+
+http://dev.mysql.com/doc/refman/5.0/en/stored-procedure-privileges.html
+
+
+=head2 Setup nebdiskd daemon
+
+Create the .netdiskrc file in the home directory of the user that nebdiskd
+will run as.  This does not need to be/should not be the root user.  Merely a
+user with permissions to see all of the approparite volumes.
+
+Example F<.netdiskrc>:
+
+    db: nebulous
+    dbpass: '@neb@'
+    dbuser: nebulous
+    mounts:
+      - /data/ipp000.0
+      - /data/ipp000.1
+      - /data/ipp002.0
+      - /data/ipp003.0
+    pidfile: /var/tmp/nebdiskd
+    poll_interval: 5
+
+Start the daemon by running the command C<nebdiskd>.  Use this command to
+verify that the daemon is working correclty.
+
+    neb-volstat
+
+nebdiskd should always been running when nebulous is use.  This command is an
+example of how to startup the daemon running as another user.  This could be
+use to start the daemon from the root account while the system init scripts
+are running.
+
+    sudo -H -u jhoblitt nebdiskd
+
+See the nebdiskd POD/man page for further details
+
+=head2 Install Apache2 with mod_perl
+
+=head3 Minimum software requirements:
+
+Nebulous will work with both Apache 1.3 & 2.x.  However, this guide will only
+cover configuraton with Apache 2.x and mod_perl 2.x.
+
+=over 4
+
+=item * Apache2 >= 2.0.54
+
+=item * mod_perl >= 2.0.0
+
+=item * Apache::DBI >= '0.97' [*]
+
+=back
+
+[*] required for connect_on_init() to work under mod_perl >= 2.0.1
+
+=head3 Apache 1.x 
+
+=item * building mod_perl-1.29 from source:
+
+    perl Makefile.PL USE_APXS=1 INSTALLDIRS=vendor WITH_APXS=/usr/sbin/apxs EVERYTHING=1 PERL_DEBUG=1
+
+    #MP_TRACE = 1 MP_DEBUG = 1 MP_USE_DSO = 1 MP_INST_APACHE2 = 1 MP_APXS = /usr/sbin/apxs2
+
+=head3 Apache 2.x 
+
+=head4 removing Apache on RHEl3
+
+On RHEL3 I had to remove these packages in order to fully remove MySQL.
+
+As C<root>:
+
+    rpm -e \
+    httpd-2.0.46-46.2.ent \
+    httpd-devel-2.0.46-46.2.ent \
+    redhat-config-httpd-1.1.0-4.30.2 \
+    mod_perl-1.99_09-10.ent \
+    mod_authz_ldap-0.22-5 \
+    mod_auth_pgsql-2.0.1-4.ent \
+    mod_python-3.0.3-5.ent \
+    mod_auth_mysql-20030510-2.ent \
+    mod_ssl-2.0.46-46.2.ent \
+    webalizer-2.01_10-15.ent \
+    mod_perl-1.99_09-10.ent \
+    mod_auth_pgsql-2.0.1-4.ent \
+    mod_python-3.0.3-5.ent \
+    mod_auth_mysql-20030510-2.ent \
+    mod_ssl-2.0.46-46.2.ent \
+    squirrelmail-1.4.3a-11.EL3 \
+    php-4.3.2-25.ent \
+    php-imap-4.3.2-25.ent \
+    php-ldap-4.3.2-25.ent \
+    php-mysql-4.3.2-25.ent \
+    php-odbc-4.3.2-25.ent \
+    php-pgsql-4.3.2-25.ent 
+
+    rm -rf /etc/httpd
+
+=head4 installing Apache2 from source
+
+    tar -jxvf httpd-2.0.54.tar.bz2
+    cd httpd-2.0.54
+    ./configure \
+    --disable-suexec \
+    --with-perl=/usr/bin/perl \
+    --with-port=80 \
+    --with-program-name=apache2 \
+    --with-devrandom=/dev/urandom \
+    --prefix           /usr \
+    --exec_prefix      /usr \
+    --bindir           /usr/bin \
+    --sbindir          /usr/sbin \
+    --libdir           /usr/lib \
+    --libexecdir       /usr/lib/apache2/modules \
+    --mandir           /usr/share/man \
+    --infodir          /usr/share/info \
+    --includedir       /usr/include/apache2 \
+    --datadir          /var/www/localhost \
+    --sysconfdir       /etc/apache2/conf \
+    --localstatedir    /var
+    make
+    su
+    make install
+    exit
+    cd ..
+
+=head4 installing mod_perl 2.x
+
+    tar -zxvf mod_perl-2.0.1.tar.gz
+    cd mod_perl-2.0.1
+    perl Makefile.PL
+    (apxs = /usr/sbin/apxs)
+    make
+    su
+    make install
+    echo "LoadModule perl_module /usr/lib/apache2/modules/mod_perl.so" >> /etc/apache2/conf/apache2.conf
+    exit
+
+    apachectl configtest
+    Syntax OK
+
+=head4 installing and configuring Nebulous
+
+    tar -zvxf Nebulous-0.01.tar.gz
+    cd Nebulous-0.01
+    perl Build.PL -axps /usr/sbin/apxs
+    ./Build
+    ./Build test
+    su
+    ./Build install
+
+
+    vi /etc/apache2/conf/startup.pl
+    (change Group to "Group nebulous")
+
+    cp fooconf /etc/apache2/conf/startup.pl
+    echo "PerlPostConfigRequire /etc/apache2/conf/startup.pl" >> /etc/apache2/conf/apache2.conf
+
+
+=head3 configurating Apache to act as a Nebulous/SOAP server
+
+=item * add the "nebulous" group to /etc/group
+=item * configure Apache to run as the nebulous group. 
+
+    Group nebulous
+
+Note that in theory the C<perchild> MPM can run vhosts as differenet
+users/groups but this module is currently non-funcitonal.
+
+L<http://httpd.apache.org/docs/trunk/mod/perchild.html>
+
+=item * configure a path for Neublous
+
+Make sure mod_perl is being loaded into Apache.  In order to do this on
+gentoo, you must edit /etc/config.d/apache2 and add C<-DPERL> to
+C<APACHE2_OTPS>.
+
+    cat <<END >> /etc/apache2/conf/apache2.conf
+    <Location /nebulous>
+        SetHandler perl-script
+        PerlResponseHandler Apache2::SOAP
+        PerlSetVar dispatch_to "PerlHandler Nebulous::Server::SOAP"
+        PerlSetVar options "compress_threshold => 10000"
+    </Location>
+    END
+
+    apachectl configtest
+    Syntax OK
+
+=item * initialize Nebulous database connections from C<startup.pl>
+
+    my $dsn         = 'DBI:mysql:database=nebulous:host=localhost';
+    my $dbuser      = 'nebulous';
+    my $dbpasswd    = '@neb@';
+
+    Apache::DBI->connect_on_init( $dsn, $dbuser, $dbpasswd );
+    Nebulous::Server::SOAP->new_on_init(
+        dsn         => $dsn,
+        dbuser      => $dbuser,
+        dbpasswd    => $dbpasswd,
+        log_level   => 'all',
+    );
+
+# out of date from here down...
+
+#
+# CGI interface
+#
+
+ScriptAlias /nebulous /usr/local/apache/perl/imageserver.pl
+
+<Directory "/usr/local/apache/perl">
+        AllowOverride None
+        Options None
+        Order allow,deny
+        Allow from all
+</Directory>
+
+PerlModule Apache::PerlRun
+<Location /perl>
+        SetHandler perl-script
+        PerlHandler Apache::PerlRun
+        Options ExecCGI
+        allow from all
+        PerlSendHeader On
+</Location>
+
+#
+# Nebulous::Apache interface
+#
+
+PerlModule Apache::DBI
+PerlModule Nebulous::Apache
+
+<Location /nebulous>
+        SetHandler perl-script
+        PerlHandler Nebulous::Apache
+</Location>
+
+- setup the server
+
+As a CGI, will with either Apache 1.3.x or 2.0.x
+
+cp scripts/imagesever.pl /var/www/localhost/cgi-bin
+chmod 755 /var/www/localhost/cgi-bin/imageserver.pl
+touch /tmp/imageserver.log
+chmod 1777 /tmp/imageserver.log
+
+=head2 Testing the Nebulous Server
+
+You can test your configuration with the C<telnet> utility.
+
+    $ telnet localhost 80
+    Trying 127.0.0.1...
+    Connected to localhost.localdomain (127.0.0.1).
+    Escape character is '^]'.
+    GET /nebulous HTTP/1.0
+
+    HTTP/1.1 500 Internal Server Error
+    Date: Wed, 31 Aug 2005 01:10:47 GMT
+    Server: Apache/2.0.54 (Unix) mod_perl/2.0.1 Perl/v5.8.6
+    Content-Length: 645
+    Connection: close
+    Content-Type: text/html; charset=iso-8859-1
+    .
+    .
+
+    Connection closed by foreign host.
+
+If you get an HTTP 5xx code check your apache error_log
+
+
+=head2 MySQL Configuration/tuning
+
+in /etc/mysql/my.cnf
+under [mysqld]
+increase the number of alloweed connections, e.g.
+max_connections = 200
+
+Nebulous makes heavy use of MySQL's C<innodb> table type.  Try configuring
+MySQL with these parameters:
+
+    innodb_log_files_in_group = 2
+    innodb_buffer_pool_size = as big as possible (512M+)
+    innodb_log_buffer_size= 16M
+    innodb_log_file_size=10M
+
+=cut
Index: anches/cnb_branches/cnb_branch_20090301/Nebulous-Server/docs/setup.txt
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Nebulous-Server/docs/setup.txt	(revision 23351)
+++ 	(revision )
@@ -1,373 +1,0 @@
-=pod
-
-=head1 Installing a Nebulous Server
-
-=head2 Create a UNIX group for Nebulous
-
-All Nebulous user's need to be in the same UNIX group as the Nebulous server.
-
-    groupadd -g 877 nebulous
-
-This is a silly hack to generate a gid.
-
-    perl -le 'map { $gid += ord } split //, shift; print $gid' nebulous
-
-=head2 Add the users that you want to be able to access Nebulous to that group
-
-    usermod -G nebulous foouser
-
-=head2 Setup directory permissions for the nebulous group
-
-As root:
-
-    mkdir /data/ipp000.0/nebulous
-    mkdir /data/ipp000.1/nebulous
-    mkdir /data/ipp002.0/nebulous
-    mkdir /data/ipp003.0/nebulous
-
-    # note that this is just an example, this won't work over NFS with
-    # root_squash enablded
-    chown nobody:nebulous /data/ipp00?.?/nebulous
-
-    chmod 0770 /data/ipp00?.?/nebulous
-    ls -lad /data/ipp00?.?/nebulous
-
-    mkdir /data/`hostname`.0/nebulous
-    chown nobody:nebulous /data/`hostname`.0/nebulous
-    chmod 0770 /data/`hostname`.0/nebulous
-
-=head2 Build and install the Nebulous Perl modules (see: README)
-
-=head2 Create and initialize the database
-
-    mysql -u root mysql -p
-    (enter your MySQL root password)
-
-    DROP DATABASE IF EXISTS nebulous;
-    CREATE DATABASE nebulous;
-    GRANT ALL PRIVILEGES ON nebulous.* TO 'nebulous'@'localhost'
-        IDENTIFIED BY '@neb@';
-    FLUSH PRIVILEGES;
-    exit
-
-    export NEB_DB=nebulous
-    export NEB_USER=nebulous
-    export NEB_PASS=@neb@
-
-    neb-initdb
-    neb-addvol --name ipp000.0 --uri file:///data/ipp000.0/nebulous
-    neb-addvol --name ipp000.1 --uri file:///data/ipp000.1/nebulous
-    neb-addvol --name ipp002.0 --uri file:///data/ipp002.0/nebulous
-    neb-addvol --name ipp003.0 --uri file:///data/ipp003.0/nebulous
-
-#    perl -e 'for (1..24) { $i = sprintf "po%02d", $_ ; system "neb-addvol --name $i --uri file:/$i/nebulous" }'
-    .
-    .
-
-If you get an error similar to this while initializing the database it means
-that the creation of the Nebulous's stored procedures failed:
-
-    ERROR 1146 (42S02): Table 'mysql.proc' doesn't exist
-
-It probably means that your instance of MySQL has been upgraded for an older
-version that did not support stored precedures.  You can fix this by running
-the C<mysql_fix_privilege_tables> script that you should have received with
-MySQL.
-
-In order for the test suite to pass the "test" account needs have the
-appropriate permissions to create and modify stored procedures.
-
-    update user set Create_routine_priv = 'Y', Alter_routine_priv = 'Y', Execute_priv = 'Y' where user = 'test';
-    update db set Create_routine_priv = 'Y', Alter_routine_priv = 'Y', Execute_priv = 'Y' where db = 'test';
-
-    flush privileges;
-
-Please see this webpage for further details:
-
-http://dev.mysql.com/doc/refman/5.0/en/stored-procedure-privileges.html
-
-
-=head2 Setup nebdiskd daemon
-
-Create the .netdiskrc file in the home directory of the user that nebdiskd
-will run as.  This does not need to be/should not be the root user.  Merely a
-user with permissions to see all of the approparite volumes.
-
-Example F<.netdiskrc>:
-
-    db: nebulous
-    dbpass: '@neb@'
-    dbuser: nebulous
-    mounts:
-      - /data/ipp000.0
-      - /data/ipp000.1
-      - /data/ipp002.0
-      - /data/ipp003.0
-    pidfile: /var/tmp/nebdiskd
-    poll_interval: 5
-
-Start the daemon by running the command C<nebdiskd>.  Use this command to
-verify that the daemon is working correclty.
-
-    neb-volstat
-
-nebdiskd should always been running when nebulous is use.  This command is an
-example of how to startup the daemon running as another user.  This could be
-use to start the daemon from the root account while the system init scripts
-are running.
-
-    sudo -H -u jhoblitt nebdiskd
-
-See the nebdiskd POD/man page for further details
-
-=head2 Install Apache2 with mod_perl
-
-=head3 Minimum software requirements:
-
-Nebulous will work with both Apache 1.3 & 2.x.  However, this guide will only
-cover configuraton with Apache 2.x and mod_perl 2.x.
-
-=over 4
-
-=item * Apache2 >= 2.0.54
-
-=item * mod_perl >= 2.0.0
-
-=item * Apache::DBI >= '0.97' [*]
-
-=back
-
-[*] required for connect_on_init() to work under mod_perl >= 2.0.1
-
-=head3 Apache 1.x 
-
-=item * building mod_perl-1.29 from source:
-
-    perl Makefile.PL USE_APXS=1 INSTALLDIRS=vendor WITH_APXS=/usr/sbin/apxs EVERYTHING=1 PERL_DEBUG=1
-
-    #MP_TRACE = 1 MP_DEBUG = 1 MP_USE_DSO = 1 MP_INST_APACHE2 = 1 MP_APXS = /usr/sbin/apxs2
-
-=head3 Apache 2.x 
-
-=head4 removing Apache on RHEl3
-
-On RHEL3 I had to remove these packages in order to fully remove MySQL.
-
-As C<root>:
-
-    rpm -e \
-    httpd-2.0.46-46.2.ent \
-    httpd-devel-2.0.46-46.2.ent \
-    redhat-config-httpd-1.1.0-4.30.2 \
-    mod_perl-1.99_09-10.ent \
-    mod_authz_ldap-0.22-5 \
-    mod_auth_pgsql-2.0.1-4.ent \
-    mod_python-3.0.3-5.ent \
-    mod_auth_mysql-20030510-2.ent \
-    mod_ssl-2.0.46-46.2.ent \
-    webalizer-2.01_10-15.ent \
-    mod_perl-1.99_09-10.ent \
-    mod_auth_pgsql-2.0.1-4.ent \
-    mod_python-3.0.3-5.ent \
-    mod_auth_mysql-20030510-2.ent \
-    mod_ssl-2.0.46-46.2.ent \
-    squirrelmail-1.4.3a-11.EL3 \
-    php-4.3.2-25.ent \
-    php-imap-4.3.2-25.ent \
-    php-ldap-4.3.2-25.ent \
-    php-mysql-4.3.2-25.ent \
-    php-odbc-4.3.2-25.ent \
-    php-pgsql-4.3.2-25.ent 
-
-    rm -rf /etc/httpd
-
-=head4 installing Apache2 from source
-
-    tar -jxvf httpd-2.0.54.tar.bz2
-    cd httpd-2.0.54
-    ./configure \
-    --disable-suexec \
-    --with-perl=/usr/bin/perl \
-    --with-port=80 \
-    --with-program-name=apache2 \
-    --with-devrandom=/dev/urandom \
-    --prefix           /usr \
-    --exec_prefix      /usr \
-    --bindir           /usr/bin \
-    --sbindir          /usr/sbin \
-    --libdir           /usr/lib \
-    --libexecdir       /usr/lib/apache2/modules \
-    --mandir           /usr/share/man \
-    --infodir          /usr/share/info \
-    --includedir       /usr/include/apache2 \
-    --datadir          /var/www/localhost \
-    --sysconfdir       /etc/apache2/conf \
-    --localstatedir    /var
-    make
-    su
-    make install
-    exit
-    cd ..
-
-=head4 installing mod_perl 2.x
-
-    tar -zxvf mod_perl-2.0.1.tar.gz
-    cd mod_perl-2.0.1
-    perl Makefile.PL
-    (apxs = /usr/sbin/apxs)
-    make
-    su
-    make install
-    echo "LoadModule perl_module /usr/lib/apache2/modules/mod_perl.so" >> /etc/apache2/conf/apache2.conf
-    exit
-
-    apachectl configtest
-    Syntax OK
-
-=head4 installing and configuring Nebulous
-
-    tar -zvxf Nebulous-0.01.tar.gz
-    cd Nebulous-0.01
-    perl Build.PL -axps /usr/sbin/apxs
-    ./Build
-    ./Build test
-    su
-    ./Build install
-
-
-    vi /etc/apache2/conf/startup.pl
-    (change Group to "Group nebulous")
-
-    cp fooconf /etc/apache2/conf/startup.pl
-    echo "PerlPostConfigRequire /etc/apache2/conf/startup.pl" >> /etc/apache2/conf/apache2.conf
-
-
-=head3 configurating Apache to act as a Nebulous/SOAP server
-
-=item * add the "nebulous" group to /etc/group
-=item * configure Apache to run as the nebulous group. 
-
-    Group nebulous
-
-Note that in theory the C<perchild> MPM can run vhosts as differenet
-users/groups but this module is currently non-funcitonal.
-
-L<http://httpd.apache.org/docs/trunk/mod/perchild.html>
-
-=item * configure a path for Neublous
-
-Make sure mod_perl is being loaded into Apache.  In order to do this on
-gentoo, you must edit /etc/config.d/apache2 and add C<-DPERL> to
-C<APACHE2_OTPS>.
-
-    cat <<END >> /etc/apache2/conf/apache2.conf
-    <Location /nebulous>
-        SetHandler perl-script
-        PerlResponseHandler Apache2::SOAP
-        PerlSetVar dispatch_to "PerlHandler Nebulous::Server::SOAP"
-        PerlSetVar options "compress_threshold => 10000"
-    </Location>
-    END
-
-    apachectl configtest
-    Syntax OK
-
-=item * initialize Nebulous database connections from C<startup.pl>
-
-    my $dsn         = 'DBI:mysql:database=nebulous:host=localhost';
-    my $dbuser      = 'nebulous';
-    my $dbpasswd    = '@neb@';
-
-    Apache::DBI->connect_on_init( $dsn, $dbuser, $dbpasswd );
-    Nebulous::Server::SOAP->new_on_init(
-        dsn         => $dsn,
-        dbuser      => $dbuser,
-        dbpasswd    => $dbpasswd,
-        log_level   => 'all',
-    );
-
-# out of date from here down...
-
-#
-# CGI interface
-#
-
-ScriptAlias /nebulous /usr/local/apache/perl/imageserver.pl
-
-<Directory "/usr/local/apache/perl">
-        AllowOverride None
-        Options None
-        Order allow,deny
-        Allow from all
-</Directory>
-
-PerlModule Apache::PerlRun
-<Location /perl>
-        SetHandler perl-script
-        PerlHandler Apache::PerlRun
-        Options ExecCGI
-        allow from all
-        PerlSendHeader On
-</Location>
-
-#
-# Nebulous::Apache interface
-#
-
-PerlModule Apache::DBI
-PerlModule Nebulous::Apache
-
-<Location /nebulous>
-        SetHandler perl-script
-        PerlHandler Nebulous::Apache
-</Location>
-
-- setup the server
-
-As a CGI, will with either Apache 1.3.x or 2.0.x
-
-cp scripts/imagesever.pl /var/www/localhost/cgi-bin
-chmod 755 /var/www/localhost/cgi-bin/imageserver.pl
-touch /tmp/imageserver.log
-chmod 1777 /tmp/imageserver.log
-
-=head2 Testing the Nebulous Server
-
-You can test your configuration with the C<telnet> utility.
-
-    $ telnet localhost 80
-    Trying 127.0.0.1...
-    Connected to localhost.localdomain (127.0.0.1).
-    Escape character is '^]'.
-    GET /nebulous HTTP/1.0
-
-    HTTP/1.1 500 Internal Server Error
-    Date: Wed, 31 Aug 2005 01:10:47 GMT
-    Server: Apache/2.0.54 (Unix) mod_perl/2.0.1 Perl/v5.8.6
-    Content-Length: 645
-    Connection: close
-    Content-Type: text/html; charset=iso-8859-1
-    .
-    .
-
-    Connection closed by foreign host.
-
-If you get an HTTP 5xx code check your apache error_log
-
-
-=head2 MySQL Configuration/tuning
-
-in /etc/mysql/my.cnf
-under [mysqld]
-increase the number of alloweed connections, e.g.
-max_connections = 200
-
-Nebulous makes heavy use of MySQL's C<innodb> table type.  Try configuring
-MySQL with these parameters:
-
-    innodb_log_files_in_group = 2
-    innodb_buffer_pool_size = as big as possible (512M+)
-    innodb_log_buffer_size= 16M
-    innodb_log_file_size=10M
-
-=cut
Index: anches/cnb_branches/cnb_branch_20090301/Nebulous-Server/docs/tmp.txt
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Nebulous-Server/docs/tmp.txt	(revision 23351)
+++ 	(revision )
@@ -1,44 +1,0 @@
-client
---
-new
-
-new_file
-                new_file
-                open_file
-
-find_file
-    find_instance
-
-open_file
-    find_file
-
-delete_file
-                delete_file
-    nuke
-
-copy_file
-    open_file
-    new_file
-
-move_file
-    copy_file
-    delete_file
-
-replicate_file
-    open_file
-                replicate_file
-
-cull_file
-    find_instance
-    delete_instance
-
-delete_instance
-    nuke
-                delete_instance
-
-find_instance
-                find_instance
-
-# missing
-file_stat
-lock ?
Index: /branches/cnb_branches/cnb_branch_20090301/Nebulous/Build.PL
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Nebulous/Build.PL	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/Nebulous/Build.PL	(revision 23352)
@@ -11,39 +11,36 @@
 my $pkg_dir = "./nebclient";
 
-sub ACTION_code {
-    my $self = shift;
-
-    $self->SUPER::ACTION_code(@_);
-
-    my $old_pwd = getcwd();
-    chdir $pkg_dir;
-
-    unless (-e "configure") {
-        system("./autogen.sh") == 0 or die "install failed: $?";
-    }
-
-    chdir $old_pwd;
-}
-
-sub ACTION_build {
-    my $self = shift;
-
-    $self->SUPER::ACTION_build(@_);
-
-    my $old_pwd = getcwd();
-    chdir $pkg_dir;
-
-    unless (-e "Makefile") {
-        system("sh ./configure") == 0 or die "build failed: $?";
-    }
-
-    system("make") == 0 or die "build failed: $?";
-
-    chdir $old_pwd;
-}
-
-# Do not attempt to install nebclient.  It is bundled in this package for
-# testing only.
-#
+# sub ACTION_code {
+#     my $self = shift;
+# 
+#     $self->SUPER::ACTION_code(@_);
+# 
+#     my $old_pwd = getcwd();
+#     chdir $pkg_dir;
+# 
+#     unless (-e "configure") {
+#         system("./autogen.sh") == 0 or die "install failed: $?";
+#     }
+# 
+#     chdir $old_pwd;
+# }
+# 
+# sub ACTION_build {
+#     my $self = shift;
+# 
+#     $self->SUPER::ACTION_build(@_);
+# 
+#     my $old_pwd = getcwd();
+#     chdir $pkg_dir;
+# 
+#     unless (-e "Makefile") {
+#         system("sh ./configure") == 0 or die "build failed: $?";
+#     }
+# 
+#     system("make") == 0 or die "build failed: $?";
+# 
+#     chdir $old_pwd;
+# }
+# 
 # sub ACTION_install {
 #     my $self = shift;
@@ -58,17 +55,17 @@
 #     chdir $old_pwd;
 # }
-
-sub ACTION_clean {
-    my $self = shift;
-
-    $self->SUPER::ACTION_clean(@_);
-
-    my $old_pwd = getcwd();
-    chdir $pkg_dir;
-
-    system("make clean") == 0 or die "install failed: $?";
-
-    chdir $old_pwd;
-}
+# 
+# sub ACTION_clean {
+#     my $self = shift;
+# 
+#     $self->SUPER::ACTION_clean(@_);
+# 
+#     my $old_pwd = getcwd();
+#     chdir $pkg_dir;
+# 
+#     system("make clean") == 0 or die "install failed: $?";
+# 
+#     chdir $old_pwd;
+# }
 EOF
 
Index: /branches/cnb_branches/cnb_branch_20090301/Nebulous/MANIFEST
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Nebulous/MANIFEST	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/Nebulous/MANIFEST	(revision 23352)
@@ -20,10 +20,4 @@
 bin/neb-touch
 bin/neb-xattr
-docs/c_api.h
-docs/database_setup.txt
-docs/design.txt
-docs/requirements.txt
-docs/setup.txt
-docs/tmp.txt
 examples/Makefile
 examples/nebexample.c
Index: /branches/cnb_branches/cnb_branch_20090301/Nebulous/nebclient/src/nebulous.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Nebulous/nebclient/src/nebulous.h	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/Nebulous/nebclient/src/nebulous.h	(revision 23352)
@@ -1,5 +1,5 @@
 /* src/nebulous.h
-   Generated by wsdl2h 1.2.11 from nebulous.wsdl and typemap.dat
-   2008-10-16 23:26:37 GMT
+   Generated by wsdl2h 1.2.12 from nebulous.wsdl and typemap.dat
+   2009-03-05 21:11:05 GMT
    Copyright (C) 2001-2008 Robert van Engelen, Genivia Inc. All Rights Reserved.
    This part of the software is released under one of the following licenses:
Index: /branches/cnb_branches/cnb_branch_20090301/Nebulous/nebclient/src/soapC.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Nebulous/nebclient/src/soapC.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/Nebulous/nebclient/src/soapC.c	(revision 23352)
@@ -1,4 +1,4 @@
 /* soapC.c
-   Generated by gSOAP 2.7.11 from src/nebulous.h
+   Generated by gSOAP 2.7.12 from src/nebulous.h
    Copyright(C) 2000-2008, Robert van Engelen, Genivia Inc. All Rights Reserved.
    This part of the software is released under one of the following licenses:
@@ -12,5 +12,5 @@
 #endif
 
-SOAP_SOURCE_STAMP("@(#) soapC.c ver 2.7.11 2008-10-16 23:26:39 GMT")
+SOAP_SOURCE_STAMP("@(#) soapC.c ver 2.7.12 2009-03-05 21:11:05 GMT")
 
 
@@ -676,6 +676,7 @@
 
 SOAP_FMAC3 char * SOAP_FMAC4 soap_in_byte(struct soap *soap, const char *tag, char *a, const char *type)
-{
-	return soap_inbyte(soap, tag, a, type, SOAP_TYPE_byte);
+{	char *p;
+	p = soap_inbyte(soap, tag, a, type, SOAP_TYPE_byte);
+	return p;
 }
 
@@ -711,6 +712,7 @@
 
 SOAP_FMAC3 int * SOAP_FMAC4 soap_in_int(struct soap *soap, const char *tag, int *a, const char *type)
-{
-	return soap_inint(soap, tag, a, type, SOAP_TYPE_int);
+{	int *p;
+	p = soap_inint(soap, tag, a, type, SOAP_TYPE_int);
+	return p;
 }
 
@@ -3384,5 +3386,5 @@
 {
 	int i, n = a->__size;
-	char *t = soap_putsize(soap, "xsd:string", a->__size);
+	char *t = a->__ptr ? soap_putsize(soap, "xsd:string", a->__size) : NULL;
 	id = soap_element_id(soap, tag, id, a, (struct soap_array*)&a->__ptr, 1, type, SOAP_TYPE_ArrayOfString);
 	if (id < 0)
@@ -4026,6 +4028,7 @@
 
 SOAP_FMAC3 char * * SOAP_FMAC4 soap_in__QName(struct soap *soap, const char *tag, char **a, const char *type)
-{
-	return soap_instring(soap, tag, a, type, SOAP_TYPE__QName, 2, -1, -1);
+{	char **p;
+	p = soap_instring(soap, tag, a, type, SOAP_TYPE__QName, 2, -1, -1);
+	return p;
 }
 
@@ -4066,6 +4069,7 @@
 
 SOAP_FMAC3 char * * SOAP_FMAC4 soap_in_string(struct soap *soap, const char *tag, char **a, const char *type)
-{
-	return soap_instring(soap, tag, a, type, SOAP_TYPE_string, 1, -1, -1);
+{	char **p;
+	p = soap_instring(soap, tag, a, type, SOAP_TYPE_string, 1, -1, -1);
+	return p;
 }
 
Index: /branches/cnb_branches/cnb_branch_20090301/Nebulous/nebclient/src/soapClient.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Nebulous/nebclient/src/soapClient.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/Nebulous/nebclient/src/soapClient.c	(revision 23352)
@@ -1,4 +1,4 @@
 /* soapClient.c
-   Generated by gSOAP 2.7.11 from src/nebulous.h
+   Generated by gSOAP 2.7.12 from src/nebulous.h
    Copyright(C) 2000-2008, Robert van Engelen, Genivia Inc. All Rights Reserved.
    This part of the software is released under one of the following licenses:
@@ -10,5 +10,5 @@
 #endif
 
-SOAP_SOURCE_STAMP("@(#) soapClient.c ver 2.7.11 2008-10-16 23:26:38 GMT")
+SOAP_SOURCE_STAMP("@(#) soapClient.c ver 2.7.12 2009-03-05 21:11:05 GMT")
 
 
Index: /branches/cnb_branches/cnb_branch_20090301/Nebulous/nebclient/src/soapClientLib.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Nebulous/nebclient/src/soapClientLib.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/Nebulous/nebclient/src/soapClientLib.c	(revision 23352)
@@ -1,4 +1,4 @@
 /* soapClientLib.c
-   Generated by gSOAP 2.7.11 from src/nebulous.h
+   Generated by gSOAP 2.7.12 from src/nebulous.h
    Copyright(C) 2000-2008, Robert van Engelen, Genivia Inc. All Rights Reserved.
    This part of the software is released under one of the following licenses:
Index: /branches/cnb_branches/cnb_branch_20090301/Nebulous/nebclient/src/soapH.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Nebulous/nebclient/src/soapH.h	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/Nebulous/nebclient/src/soapH.h	(revision 23352)
@@ -1,4 +1,4 @@
 /* soapH.h
-   Generated by gSOAP 2.7.11 from src/nebulous.h
+   Generated by gSOAP 2.7.12 from src/nebulous.h
    Copyright(C) 2000-2008, Robert van Engelen, Genivia Inc. All Rights Reserved.
    This part of the software is released under one of the following licenses:
Index: /branches/cnb_branches/cnb_branch_20090301/Nebulous/nebclient/src/soapServer.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Nebulous/nebclient/src/soapServer.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/Nebulous/nebclient/src/soapServer.c	(revision 23352)
@@ -1,4 +1,4 @@
 /* soapServer.c
-   Generated by gSOAP 2.7.11 from src/nebulous.h
+   Generated by gSOAP 2.7.12 from src/nebulous.h
    Copyright(C) 2000-2008, Robert van Engelen, Genivia Inc. All Rights Reserved.
    This part of the software is released under one of the following licenses:
@@ -10,5 +10,5 @@
 #endif
 
-SOAP_SOURCE_STAMP("@(#) soapServer.c ver 2.7.11 2008-10-16 23:26:38 GMT")
+SOAP_SOURCE_STAMP("@(#) soapServer.c ver 2.7.12 2009-03-05 21:11:05 GMT")
 
 
Index: /branches/cnb_branches/cnb_branch_20090301/Nebulous/nebclient/src/soapServerLib.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Nebulous/nebclient/src/soapServerLib.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/Nebulous/nebclient/src/soapServerLib.c	(revision 23352)
@@ -1,4 +1,4 @@
 /* soapServerLib.c
-   Generated by gSOAP 2.7.11 from src/nebulous.h
+   Generated by gSOAP 2.7.12 from src/nebulous.h
    Copyright(C) 2000-2008, Robert van Engelen, Genivia Inc. All Rights Reserved.
    This part of the software is released under one of the following licenses:
Index: /branches/cnb_branches/cnb_branch_20090301/Nebulous/nebclient/src/soapStub.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Nebulous/nebclient/src/soapStub.h	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/Nebulous/nebclient/src/soapStub.h	(revision 23352)
@@ -1,4 +1,4 @@
 /* soapStub.h
-   Generated by gSOAP 2.7.11 from src/nebulous.h
+   Generated by gSOAP 2.7.12 from src/nebulous.h
    Copyright(C) 2000-2008, Robert van Engelen, Genivia Inc. All Rights Reserved.
    This part of the software is released under one of the following licenses:
Index: /branches/cnb_branches/cnb_branch_20090301/Nebulous/scripts/ptest.pl
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Nebulous/scripts/ptest.pl	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/Nebulous/scripts/ptest.pl	(revision 23352)
@@ -5,27 +5,35 @@
 
 use lib "./lib";
+package main;
 
-use Nebulous::Client;
+#use Nebulous::Client;
 use IO::Select;
 use IO::Socket;
+use POSIX qw(:DEFAULT :sys_wait_h);
+use Hook::LexWrap;
+use Sys::Hostname;
 
-my $neb = Nebulous::Client->new(
-#    proxy   => 'http://localhost:80/nebulous'
-    proxy   => 'http://alala:80/nebulous'
-);
+my $print_stdout = undef;
+
+$| = 1;
 
 my $key = shift || 'foobar';
 my $kids = shift || 1;
 
-my $s = IO::Select->new();
+$key = "/tmp/" . $key;
+
+#my $s = IO::Select->new();
 
 foreach my $id ( 1..$kids ) {
-    my ($sock_parent, $sock_child) = IO::Socket->socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC);
-    $s->add($sock_parent);
+#    my ($sock_parent, $sock_child) = IO::Socket->socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC);
+#    $s->add($sock_parent);
 
     my $pid = fork;
 
     unless ( $pid )  {
+        # child
+        my $sock_child = \*STDOUT;
         child($sock_child, $id);
+        shutdown($sock_child, 2);
         exit 0;
     }
@@ -33,55 +41,70 @@
 }
 
-while ($kids) {
-    foreach my $child ($s->can_read(1)) {
-        my $string = <$child>;
-        my @events = split(/\n/, $string) if $string;
-        print join("\n", @events), "\n" if scalar @events;
+$SIG{CHLD} = \&REAPER;
+
+sub REAPER {
+    while ((my $pid = waitpid(-1,WNOHANG)) > 0) {
+        $kids--;
+#        delete $children{$pid};
     }
+    $SIG{CHLD} = \&REAPER;
 }
 
-#while ( $kids ) {
-#    wait();
-#    $kids --;
-#}
 
-our $my_sock;
+while ($kids) {
+#    foreach my $child ($s->can_read(0)) {
+#        my $string = do { local $/; <$child>};
+#        my @events = split(/\n/, $string) if $string;
+#        print join("\n", @events), "\n" if scalar @events;
+#    }
+}
 
 sub child
 {
-    my ($sock_child, $id) = @_;
+    my ($sock, $id) = @_;
 
-    $my_sock = $sock_child;
+    unless ($print_stdout) {
+        my $filename = hostname() . "." . $$ . ".txt";
+        open my $fh, ">$filename" or die "can't open $filename: $!";
+
+        open STDOUT, ">&", $fh or die "can't reopen STDOUT: $!";
+        autoflush STDOUT 1;
+    }
+
+#    select $sock;
+#    $| = 1;
 
     # child
     my $fname = "${key}_$id";
-    print $sock_child "$$ : i'm a little tea pot using key: $fname\n";
-    my $fh = $neb->open_create( $fname );
-    child_die("can't create file $fname") unless $fh;
 
-    print $fh "fooby\n";
+    my $neb = Nebulous::Client::Bench->new(
+#    proxy   => 'http://localhost:80/nebulous'
+        proxy   => 'http://alala:80/nebulous',
+#        sock    => \*STDOUT,
+    );
 
-    close $fh;
 
-    $fh = $neb->open( $fname, 'read' ) or child_die("can't open file");
-    close $fh;
+    while (1) {
+#    print $sock "$$ : i'm a little tea pot using key: $fname\n";
+        my $fh = $neb->open_create( $fname )
+            or child_die($sock, "can't create file $fname");
+        close $fh;
 
-    $neb->lock( $fname, 'read' );
-    $neb->unlock( $fname, 'read' );
-    $neb->replicate( $fname );
-    $neb->cull( $fname );
+        $fh = $neb->open( $fname, 'read' )
+            or child_die("can't open file");
+        close $fh;
 
-    print $sock_child "$$ : half way\n";
+        $neb->lock( $fname, 'read' );
+        $neb->unlock( $fname, 'read' );
+        $neb->replicate( $fname );
+        $neb->cull( $fname );
+        $neb->find( $fname );
+        $neb->copy( $fname, $fname . "_copy" );
+        $neb->move( $fname, $fname . "_move" );
+        $neb->delete( $fname . "_copy" );
+        $neb->delete( $fname . "_move" );
 
-    $neb->find( $fname );
-    $neb->copy( $fname, $fname . "_copy" );
-    $neb->move( $fname, $fname . "_move" );
-    $neb->delete( $fname . "_copy" );
-    $neb->delete( $fname . "_move" );
-
-    print $sock_child "$$ : all done!\n";
-
-    $sock_child->flush;
-    sleep 10;
+#    print $sock "$$ : all done!\n";
+    }
 
     return 1;
@@ -90,6 +113,65 @@
 sub child_die
 {
-    print $my_sock $@;
-    shutdown($my_sock, 2);
+    my $sock = shift;
+    print $sock @_;
+    shutdown($sock, 2);
     exit 1;
 }
+
+package Nebulous::Client::Bench;
+
+use base qw( Nebulous::Client );
+
+#sub new
+#{
+#    my $class = shift;
+#    my %p = @_;
+#
+#    my $sock = delete $p{sock};
+#    my $self = $class->SUPER::new(%p);
+#    $self->{sock} = $sock;
+#
+#    return $self;
+#}
+
+BEGIN {
+sub make_wrapper
+{
+    my $method = shift;
+
+eval "sub $method {"
+.'    my $self = shift;'
+.'    my $smark = Time::HiRes::time();'
+.'    my $ret = $self->SUPER::' . "$method" .'(@_);'
+.'    my $emark = Time::HiRes::time();'
+.'    printf "%-17s %-17s %s\n", $emark, " ' . "$method" . ' ", ($emark - $smark), "\n";'
+.'    return $ret;'
+.'}';
+
+}
+
+make_wrapper("create");
+make_wrapper("open_create");
+make_wrapper("replicate");
+make_wrapper("cull");
+make_wrapper("lock");
+make_wrapper("unlock");
+make_wrapper("setxattr");
+make_wrapper("getxattr");
+make_wrapper("listxattr");
+make_wrapper("removexattr");
+make_wrapper("find_objects");
+make_wrapper("find_instances");
+#make_wrapper("find");
+#make_wrapper("open");
+#make_wrapper("delete");
+#make_wrapper("copy");
+make_wrapper("move");
+make_wrapper("swap");
+make_wrapper("delete_instance");
+make_wrapper("stat");
+make_wrapper("mounts");
+
+}
+
+1;
Index: /branches/cnb_branches/cnb_branch_20090301/Nebulous/scripts/stats.pl
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Nebulous/scripts/stats.pl	(revision 23352)
+++ /branches/cnb_branches/cnb_branch_20090301/Nebulous/scripts/stats.pl	(revision 23352)
@@ -0,0 +1,50 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+
+use Statistics::Descriptive;
+
+#my $filename = shift or die "Usage: stats.pl <filename>";
+my $filename = shift || "-";
+
+open(my $fh, "$filename") or die "can't open file $filename: $!";
+
+my $data = do {local $/; <$fh>};
+
+my %methods;
+
+foreach my $line (split(/\n/, $data)) {
+    my ($time, $method, $duration) = split(/\s+/, $line);
+
+    push @{$methods{$method}->{time}}, $time;
+    push @{$methods{$method}->{duration}}, $duration;
+}
+
+close($fh) or die "can't close file $filename: $!";
+
+use Data::Dumper;
+
+my $total_stat = Statistics::Descriptive::Sparse->new();
+#print Dumper \%methods;
+foreach my $method (keys %methods) {
+    {
+        my $duration    = $methods{$method}->{duration};
+        my $stat = Statistics::Descriptive::Sparse->new();
+        $stat->add_data(@$duration);
+        printf("%-40s %f\n", "$method: mean duration: ", $stat->mean());
+    }
+    {
+        my $times       = $methods{$method}->{time};
+        my $stat = Statistics::Descriptive::Sparse->new();
+        $stat->add_data(@$times);
+        $total_stat->add_data(@$times);
+        printf("%-40s %f\n","$method: calls/s: ", $stat->count / ($stat->max - $stat->min));
+    }
+}
+
+print "max : ", $total_stat->max, "\n";
+print "min : ", $total_stat->min, "\n";
+print "total s : ", ($total_stat->max - $total_stat->min), "\n";
+print "count : ", $total_stat->count, "\n";
+print "total : calls/s: ", $total_stat->count / ($total_stat->max - $total_stat->min), "\n";
Index: /branches/cnb_branches/cnb_branch_20090301/Ohana/Makefile.in
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Ohana/Makefile.in	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/Ohana/Makefile.in	(revision 23352)
@@ -75,5 +75,5 @@
 	mkdir -p $(DESTLIB)
 	mkdir -p $(DESTBIN)
-	for i in $(LIBS); do make $$i.install || exit; done
+	for i in $(LIBS); do make $$i.install || exit 1; done
 	chmod +x ohana-config
 	cp -f ohana-config $(DESTBIN)/
@@ -83,31 +83,31 @@
 
 all:
-	make libs || exit
-	for i in $(PROGRAM); do make $$i || exit; done
+	make libs || exit 1
+	for i in $(PROGRAM); do make $$i || exit 1; done
 
 extras:
-	for i in $(EXTRAS); do make $$i || exit; done
+	for i in $(EXTRAS); do make $$i || exit 1; done
 
 pantasks:
-	make libs || exit
-	cd src/opihi; make pclient.install && exit
-	cd src/opihi; make pcontrol.install && exit
-	cd src/opihi; make pantasks.install && exit
+	make libs || exit 1
+	cd src/opihi; make pclient.install && exit 1
+	cd src/opihi; make pcontrol.install && exit 1
+	cd src/opihi; make pantasks.install && exit 1
 
 mana:
 	make libs
 	make kapa2.install
-	cd src/opihi; make mana.install && exit
+	cd src/opihi; make mana.install && exit 1
 
 dvoshell:
 	make libs
 	make kapa2.install
-	cd src/opihi; make dvo.install && exit
+	cd src/opihi; make dvo.install && exit 1
 
 clean:
 	@if [ "$(ARCH)" = "" ]; then echo ""; echo " *** please define ARCH ***"; echo; exit 1; fi
-	for i in $(LIBS); do make $$i.clean || exit; done
-	for i in $(PROGRAM); do make $$i.clean || exit; done
-	for i in $(EXTRAS); do make $$i.clean || exit; done
+	for i in $(LIBS); do make $$i.clean || exit 1; done
+	for i in $(PROGRAM); do make $$i.clean || exit 1; done
+	for i in $(EXTRAS); do make $$i.clean || exit 1; done
 	@rm -f `find . -name .mana`
 	@rm -f `find . -name .dvo`
@@ -116,16 +116,16 @@
 dist:
 	@if [ "$(ARCH)" = "" ]; then echo ""; echo " *** please define ARCH ***"; echo; exit 1; fi
-	for i in $(LIBS); do make $$i.dist || exit; done
-	for i in $(PROGRAM); do make $$i.dist || exit; done
+	for i in $(LIBS); do make $$i.dist || exit 1; done
+	for i in $(PROGRAM); do make $$i.dist || exit 1; done
 	@echo -n -e "\0033]0; *** Ohana: done $@ *** \0007" \
 
 install:
 	@if [ "$(ARCH)" = "" ]; then echo ""; echo " *** please define ARCH ***"; echo; exit 1; fi
-	for i in $(LIBS); do make $$i.install || exit; done
-	for i in $(PROGRAM); do make $$i.install || exit; done
+	for i in $(LIBS); do make $$i.install || exit 1; done
+	for i in $(PROGRAM); do make $$i.install || exit 1; done
 	@echo -n -e "\0033]0; *** Ohana: done $@ *** \0007" \
 
 install.extras:
-	for i in $(EXTRAS); do make $$i.install || exit; done
+	for i in $(EXTRAS); do make $$i.install || exit 1; done
 	@echo -n -e "\0033]0; *** Ohana: done $@ *** \0007" \
 
Index: /branches/cnb_branches/cnb_branch_20090301/Ohana/src/addstar/include/skycells.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Ohana/src/addstar/include/skycells.h	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/Ohana/src/addstar/include/skycells.h	(revision 23352)
@@ -12,5 +12,5 @@
 # include <glob.h>
 
-enum {SQUARES, TRIANGLES, LOCAL};
+enum {SQUARES, TRIANGLES, LOCAL, RINGS};
 enum {TETRAHEDRON, CUBE, OCTOHEDRON, DODECAHEDRON, ICOSAHEDRON};
 
@@ -67,4 +67,5 @@
 double SCALE;
 double PADDING;
+double CELLSIZE;
 int    LEVEL;
 
@@ -84,12 +85,19 @@
 int 	     sky_tessellation 	       	    PROTO((FITS_DB *db, int level, int Nmax, int mode, double scale));
 int          sky_tessellation_init          PROTO((double scale));
+
 int 	     sky_tessellation_local         PROTO((FITS_DB *db, int level, int Nmax));
 int 	     sky_tessellation_triangles     PROTO((FITS_DB *db, int level, int Nmax));
 int 	     sky_tessellation_squares       PROTO((FITS_DB *db, int level, int Nmax));
+int          sky_tessellation_rings         PROTO((FITS_DB *db, int level, int Nmax));
+
 int 	     sky_triangle_to_image     	    PROTO((Image *image, SkyTriangle *triangle));
 int 	     sky_triangle_to_rectangle 	    PROTO((SkyRectangle *image, SkyTriangle *triangle));
+
 int          sky_rectangle_local            PROTO((SkyRectangle *rectangle));
 int 	     sky_subdivide_image       	    PROTO((Image *output, SkyRectangle *input, int Nx, int Ny));
 int 	     sky_triangle_coords       	    PROTO((SkyTriangle *triangle));
+
+SkyRectangle *sky_rectangle_ring            PROTO((float dec, float dDEC, int *nring));
+
 SkyTriangle *sky_divide_triangles      	    PROTO((SkyTriangle *in, int *ntriangles));
 SkyTriangle *sky_base_triangles        	    PROTO((int *ntriangles));
Index: /branches/cnb_branches/cnb_branch_20090301/Ohana/src/addstar/src/args_skycells.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Ohana/src/addstar/src/args_skycells.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/Ohana/src/addstar/src/args_skycells.c	(revision 23352)
@@ -35,4 +35,7 @@
       MODE = LOCAL;
     }
+    if (!strcasecmp (argv[N], "rings")) {
+      MODE = RINGS;
+    }
     remove_argument (N, &argc, argv);
   }
@@ -140,12 +143,25 @@
 
   LEVEL = 8;
-  if ((MODE != LOCAL) && (N = get_argument (argc, argv, "-level"))) {
-    remove_argument (N, &argc, argv);
-    LEVEL = strtol (argv[N], &ptr, 10);
-    remove_argument (N, &argc, argv);
-    if ((*ptr != 0) || (LEVEL < 0)) {
-      fprintf (stderr, "-level requires an integer (>= 0) argument\n");
+  if ((MODE == SQUARES) || (MODE == TRIANGLES)) {
+    if ((N = get_argument (argc, argv, "-level"))) {
+      remove_argument (N, &argc, argv);
+      LEVEL = strtol (argv[N], &ptr, 10);
+      remove_argument (N, &argc, argv);
+      if ((*ptr != 0) || (LEVEL < 0)) {
+	fprintf (stderr, "-level requires an integer (>= 0) argument\n");
+	help ();
+      }  
+    }
+  }
+
+  CELLSIZE = 4.0;
+  if ((MODE == RINGS) && (N = get_argument (argc, argv, "-cellsize"))) {
+    remove_argument (N, &argc, argv);
+    CELLSIZE = strtod (argv[N], &ptr);
+    if ((*ptr != 0) || (CELLSIZE < 0.0)) {
+      fprintf (stderr, "-level requires a floating-point argument\n");
       help ();
     }  
+    remove_argument (N, &argc, argv);
   }
 
Index: /branches/cnb_branches/cnb_branch_20090301/Ohana/src/addstar/src/get2mass_full.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Ohana/src/addstar/src/get2mass_full.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/Ohana/src/addstar/src/get2mass_full.c	(revision 23352)
@@ -1,4 +1,6 @@
 # include "addstar.h"
 # include "2mass.h"
+
+// XXX check to see if desired output format is PS1_V1 or later?  (use 16bit version if not?)
 
 // fill in the data for a JHK triplet star.  takes a pointer to the start of the line the
@@ -171,12 +173,12 @@
 
   switch (qual) {
-    case 'X': star[0].measure.photFlags |= 0x0000; break;
-    case 'U': star[0].measure.photFlags |= 0x0001; break;
-    case 'F': star[0].measure.photFlags |= 0x0002; break;
-    case 'E': star[0].measure.photFlags |= 0x0003; break;
-    case 'A': star[0].measure.photFlags |= 0x0004; break;
-    case 'B': star[0].measure.photFlags |= 0x0005; break;
-    case 'C': star[0].measure.photFlags |= 0x0006; break;
-    case 'D': star[0].measure.photFlags |= 0x0007; break;
+    case 'A': star[0].measure.photFlags |= 0x00000001; break; // was: 0x0004
+    case 'B': star[0].measure.photFlags |= 0x00000002; break; // was: 0x0005
+    case 'C': star[0].measure.photFlags |= 0x00000004; break; // was: 0x0006
+    case 'D': star[0].measure.photFlags |= 0x00000008; break; // was: 0x0007
+    case 'E': star[0].measure.photFlags |= 0x00000010; break; // was: 0x0003
+    case 'F': star[0].measure.photFlags |= 0x00000020; break; // was: 0x0002
+    case 'U': star[0].measure.photFlags |= 0x00000040; break; // was: 0x0001
+    case 'X': star[0].measure.photFlags |= 0x00000080; break; // was: 0x0000
     default: 
       fprintf (stderr, "error!\n");
@@ -189,11 +191,11 @@
 
   switch (qual) {
-    case '0': star[0].measure.photFlags |= 0x0000; break;
-    case '1': star[0].measure.photFlags |= 0x0010; break;
-    case '2': star[0].measure.photFlags |= 0x0020; break;
-    case '3': star[0].measure.photFlags |= 0x0030; break;
-    case '4': star[0].measure.photFlags |= 0x0040; break;
-    case '6': star[0].measure.photFlags |= 0x0050; break;
-    case '9': star[0].measure.photFlags |= 0x0060; break;
+    case '0': star[0].measure.photFlags |= 0x00000100; break; // was: 0x0000 
+    case '1': star[0].measure.photFlags |= 0x00000200; break; // was: 0x0010 
+    case '2': star[0].measure.photFlags |= 0x00000400; break; // was: 0x0020 
+    case '3': star[0].measure.photFlags |= 0x00000800; break; // was: 0x0030 
+    case '4': star[0].measure.photFlags |= 0x00001000; break; // was: 0x0040 
+    case '6': star[0].measure.photFlags |= 0x00002000; break; // was: 0x0050 
+    case '9': star[0].measure.photFlags |= 0x00004000; break; // was: 0x0060 
     default: 
       fprintf (stderr, "error!\n");
@@ -206,10 +208,10 @@
 
   switch (qual) {
-    case 'p': star[0].measure.photFlags |= 0x0000; break;
-    case 'c': star[0].measure.photFlags |= 0x0100; break;
-    case 'd': star[0].measure.photFlags |= 0x0200; break;
-    case 's': star[0].measure.photFlags |= 0x0300; break;
-    case 'b': star[0].measure.photFlags |= 0x0400; break;
-    case '0': star[0].measure.photFlags |= 0x0500; break;
+    case 'p': star[0].measure.photFlags |= 0x00010000; break; // was: 0x0000
+    case 'c': star[0].measure.photFlags |= 0x00020000; break; // was: 0x0100
+    case 'd': star[0].measure.photFlags |= 0x00040000; break; // was: 0x0200
+    case 's': star[0].measure.photFlags |= 0x00080000; break; // was: 0x0300
+    case 'b': star[0].measure.photFlags |= 0x00010000; break; // was: 0x0400
+    case '0': star[0].measure.photFlags |= 0x00020000; break; // was: 0x0500
     default: 
       fprintf (stderr, "error!\n");
@@ -222,7 +224,7 @@
 
   switch (qual) {
-    case '0': star[0].measure.photFlags &= ~0x0008; break;
-    case '1': star[0].measure.photFlags &= ~0x0008; break;
-    default:  star[0].measure.photFlags |=  0x0008; break;
+    case '0': star[0].measure.photFlags &= ~0x00300000; break; // was: ~0x0008
+    case '1': star[0].measure.photFlags |=  0x00100000; break; // was: ~0x0008
+    default:  star[0].measure.photFlags |=  0x00200000; break; // was:  0x0008
   }      
   return (TRUE);
@@ -232,8 +234,7 @@
 
   switch (qual) {
-    case '0': star[0].measure.photFlags &= ~0x0080; break;
-    case '1': star[0].measure.photFlags &= ~0x0080; break;
-    default:  
-      star[0].measure.photFlags |= 0x0080; 
+    case '0': star[0].measure.photFlags &= ~0x00c00000; break; // was: ~0x0080 
+    case '1': star[0].measure.photFlags |=  0x00400000; break; // was: ~0x0080 
+    default:  star[0].measure.photFlags |=  0x00800000;	       // was:  0x0080
       star[0].measure.extNsigma = 100.0;
       break;
@@ -245,7 +246,7 @@
 
   switch (qual) {
-    case '0': star[0].measure.photFlags &= ~0x0800; break;
-    case '1': star[0].measure.photFlags &= ~0x0800; break;
-    default:  star[0].measure.photFlags |=  0x0800; break;
+    case '0': star[0].measure.photFlags &= ~0x03000000; break; // was: ~0x0800
+    case '1': star[0].measure.photFlags |=  0x01000000; break; // was: ~0x0800
+    default:  star[0].measure.photFlags |=  0x02000000; break; // was:  0x0800
   }      
   return (TRUE);
@@ -255,7 +256,7 @@
 
   switch (qual) {
-    case '0': star[0].measure.photFlags &= ~0x1000; break;
-    case '1': star[0].measure.photFlags &= ~0x1000; break;
-    default:  star[0].measure.photFlags |=  0x1000; break;
+    case '0': star[0].measure.photFlags &= ~0x0c000000; break; // was: ~0x1000
+    case '1': star[0].measure.photFlags |=  0x04000000; break; // was: ~0x1000
+    default:  star[0].measure.photFlags |=  0x08000000; break; // was:  0x1000
   }      
   return (TRUE);
@@ -265,6 +266,6 @@
 
   switch (qual) {
-    case '0': star[0].measure.photFlags &= ~0x2000; break;
-    case '1': star[0].measure.photFlags |=  0x2000; break;
+    case '0': star[0].measure.photFlags &= ~0x10000000; break; // was: ~0x2000
+    case '1': star[0].measure.photFlags |=  0x10000000; break; // was:  0x2000
     default:  abort();
   }      
@@ -272,2 +273,9 @@
 }
 
+// unused photFlags:
+// 0x0000.8000
+// 0x0004.0000
+// 0x0008.0000
+// 0x2000.0000
+// 0x4000.0000
+// 0x8000.0000
Index: /branches/cnb_branches/cnb_branch_20090301/Ohana/src/addstar/src/sky_tessalation.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Ohana/src/addstar/src/sky_tessalation.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/Ohana/src/addstar/src/sky_tessalation.c	(revision 23352)
@@ -11,17 +11,19 @@
   sky_tessellation_init (scale);
 
-  if (mode == SQUARES) {
-    sky_tessellation_squares (db, level, Nmax);
-    return TRUE;
-  }
-
-  if (mode == TRIANGLES) {
-    sky_tessellation_triangles (db, level, Nmax);
-    return TRUE;
-  }
-
-  if (mode == LOCAL) {
-    sky_tessellation_local (db, level, Nmax);
-    return TRUE;
+  switch (mode) {
+    case SQUARES:
+      sky_tessellation_squares (db, level, Nmax);
+      return TRUE;
+    case TRIANGLES:
+      sky_tessellation_triangles (db, level, Nmax);
+      return TRUE;
+    case LOCAL:
+      sky_tessellation_local (db, level, Nmax);
+      return TRUE;
+    case RINGS:
+      sky_tessellation_rings (db, level, Nmax);
+      return TRUE;
+    default:
+      break;
   }
 
@@ -222,4 +224,53 @@
 
   free (image);
+  return (TRUE);
+}
+
+// the RINGS tessellation uses the declination zones proposed by Tamas Budavari
+// we generate projects on uniform rings of constant dec height
+int sky_tessellation_rings (FITS_DB *db, int level, int Nmax) {
+
+  int j, nDEC, Nimage, Nring;
+  float dec, dDEC;
+  SkyRectangle *ring;
+  Image *image;
+
+  // The tessellation has one input parameter: the approximate cell size.  Starting with
+  // the cell size, determine the optimal projection cell height (dDEC) that results in an
+  // integer number of dec zones between -90 and +90
+
+  // in fact, we place a single image on each pole, so the real range of dec is 180.0 - CELLSIZE:
+
+  nDEC = (180.0 - CELLSIZE) / CELLSIZE;
+  dDEC = (180.0 - CELLSIZE) / nDEC;
+  nDEC += 2;
+
+  // a test
+  // for (dec = 0.0 + 0.5*dDEC; dec < +90.0; dec += dDEC) {
+
+  // generate the a collection of rectangles for each ring
+  for (dec = -90.0; dec < +90.0 + 0.5*dDEC; dec += dDEC) {
+
+    ring = sky_rectangle_ring (dec, dDEC, &Nring);
+    if (!ring) continue;
+
+    // subdivide each image (Nx x Ny subcells)
+    Nimage = NX_SUB*NY_SUB*Nring;
+    ALLOCATE (image, Image, Nimage);
+    for (j = 0; j < Nring; j++) {
+      // convert the SkyRectangles to Images for output
+      sky_subdivide_image (&image[j*NX_SUB*NY_SUB], &ring[j], NX_SUB, NY_SUB);
+    }
+
+    /* add the new images and save */
+    dvo_image_addrows (db, image, Nimage);
+    SetProtect (TRUE);
+    dvo_image_update (db, VERBOSE);
+    SetProtect (FALSE);
+    dvo_image_clear_vtable (db);
+    
+    free (ring);
+    free (image);
+  }    
   return (TRUE);
 }
@@ -480,4 +531,116 @@
 
   return (TRUE);
+}
+
+// define the parameters of a single sky projection center
+SkyRectangle *sky_rectangle_ring (float dec, float dDEC, int *nring) {
+
+  int i, NX, NY, nRA;
+  SkyRectangle *ring;
+  float theta, dRA;
+
+  // 'dec' is a guess at the center of the cell; in fact, we need to choose decLower and
+  // decUpper to ensure complete overlap of the cells
+
+  // we can determine the 'lower' bound (bound closest to the equator):
+  float decLower = (dec > 0.0) ? dec - 0.5*dDEC : dec + 0.5*dDEC;
+
+  // solve for actual cellsize (\theta):  tan(\delta_{n+1} - \theta/2) = tan(\delta_n + \theta/2)cos(\alpha_n / 2)
+  float decUpper = (dec > 0.0) ? dec + dDEC : dec - dDEC;
+
+  if (fabs(dec) + 0.5*dDEC > 90.0) {
+    // onPole = TRUE;
+    theta = dDEC;
+    nRA = 1;
+    dRA = theta / cos(decLower*RAD_DEG); // make a square at the pole
+  } else {
+    // onPole = FALSE;
+    // Subdivide the 'lower' bound into an integer number of segments:
+    nRA = cos(RAD_DEG*decLower) * 360.0 / CELLSIZE; // CELLSIZE is a projection size
+    dRA = 360.0 / nRA;                         // dRA is a size in RA degrees == \alpha_n
+
+    // tan(decUpper - theta/2) = tan(dec + theta/2) cos(dRA / 2);
+
+    // we solve this equation for theta (fairly ugly: expand the tangents into sin/cos, expand the 
+    // sum-of-angle sine and cosine, multiply through, convert via half-angle formulae and write 
+    // as a quadratic expression in sine(theta/2)
+  
+    float sd1 = sin(RAD_DEG*decUpper);
+    float cd1 = cos(RAD_DEG*decUpper);
+    float sd2 = sin(RAD_DEG*dec);
+    float cd2 = cos(RAD_DEG*dec);
+    float   k = cos(RAD_DEG*dRA/2.0);
+
+    float c1 =  (sd1*cd2 + sd2*cd1)*(1.0 - k);
+    float c2 =  (sd1*cd2 - sd2*cd1)*(1.0 + k);
+    float c3 = -(sd1*sd2 + cd1*cd2)*(1.0 + k); 
+
+    float A = SQ(c3) + SQ(c2);
+    float B = 2*c1*c3;
+    float C = SQ(c1) - SQ(c2);
+
+    float arg = SQ(B) - 4.0*A*C;
+
+    float root;
+
+    if (dec >= 0.0) {
+      root = (-B + sqrt (arg)) / (2.0*A);
+      theta = +DEG_RAD*asin(root);
+    } else {
+      root = (-B - sqrt (arg)) / (2.0*A);
+      theta = -DEG_RAD*asin(root);
+    }
+
+    // the negative solution yields a negative cellsize 
+    // float root2 = (-B - sqrt (arg)) / (2.0*A);
+    // float theta2 = DEG_RAD*asin(root2);
+
+    // test lines:
+    // float r1 = tan(RAD_DEG*(decUpper - 0.5*theta1));
+    // float r2 = tan(RAD_DEG*(dec + 0.5*theta1));
+    // fprintf (stdout, "%f %f  %f  %f  %f %f  %f %f  %f %f %f\n", dec, decUpper, dRA, arg, root1, root2, theta1, theta2, r1, r2, k*r2);
+  }
+  fprintf (stdout, "%f %f  %f x %f (%d)\n", dec, decUpper, dRA, theta, nRA);
+
+  // I think we need to return the value of dec for the next ring, but I am not sure...
+
+  ALLOCATE (ring, SkyRectangle, nRA);
+
+  for (i = 0; i < nRA; i++) {
+    memset (&ring[i], 0, sizeof(SkyRectangle));
+    memset (&ring[i].coords, 0, sizeof(Coords));
+    ring[i].coords.crval1 = i*dRA;
+    ring[i].coords.crval2 = dec;
+
+    ring[i].coords.pc1_1 = +1.0;
+    ring[i].coords.pc1_2 = +0.0;
+    ring[i].coords.pc2_1 = -0.0;
+    ring[i].coords.pc2_2 = +1.0;
+  
+    // range values are in projected degrees
+    NX = cos(decLower*RAD_DEG) * dRA   * 3600.0 / SCALE;
+    NY =                         theta * 3600.0 / SCALE;
+
+    // crpix1,crpix2 is the projection center
+    ring[i].coords.crpix1 = 0.5*NX;
+    ring[i].coords.crpix2 = 0.5*NY;
+
+    ring[i].coords.cdelt1 = SCALE / 3600.0;
+    ring[i].coords.cdelt2 = SCALE / 3600.0;
+
+    strcpy (ring[i].coords.ctype, "DEC--TAN");
+
+    ring[i].NX = NX;
+    ring[i].NY = NY;
+    ring[i].photcode = 1; // this needs to be set more sensibly
+
+
+    // fprintf (stderr, "%f %f  : %f %f\n", 
+    // ring[i].coords.crval1, ring[i].coords.crval2, 
+    // ring[i].coords.crpix1, ring[i].coords.crpix2);
+  }
+
+  *nring = nRA;
+  return (ring);
 }
 
Index: /branches/cnb_branches/cnb_branch_20090301/Ohana/src/libohana/src/CommOps.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Ohana/src/libohana/src/CommOps.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/Ohana/src/libohana/src/CommOps.c	(revision 23352)
@@ -14,4 +14,5 @@
     return (status);
   }
+  // fprintf (stderr, "resp: %s\n", command.buffer);
 
   /* buffer contains an EOL NULL, we can just sscan it */
Index: /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/dimm/Analysis.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/dimm/Analysis.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/dimm/Analysis.c	(revision 23352)
@@ -86,4 +86,83 @@
 	}
 
+/* find contiguous trigger pixels in row from starting point */
+
+int fillrow (float *buffer, int Nx, int offset, int sx, int *xs, int *xe) {
+
+  trigger = FALSE;
+  for (i = sx, pix = offset + i; buffer[pix] && (i < Nx); i++, pix++) {
+    addpix (pix);
+    buffer[pix] = 0;
+    trigger = TRUE;
+    *xe = i;
+  }
+  for (i = sx - 1, pix = offset + i; (i >= 0) && buffer[pix]; i--, pix--) {
+    addpix (pix);
+    buffer[pix] = 0;
+    trigger = TRUE;
+    *xs = i;
+  }
+  return (trigger);
+}
+
+static int Npix = 0;
+static int *Pix = (int *) NULL;
+
+addpix (int pix) {
+  Npix ++;
+  if (Pix == (int *) NULL) {
+    ALLOCATE (Pix, int, MAX (1, Npix));
+  } else {
+    REALLOCATE (Pix, int, MAX (1, Npix));
+  }    
+  Pix[Npix - 1] = pix;
+}
+
+clearpix () {
+  Npix = 0;
+  REALLOCATE (Pix, int, 1);
+}
+
+statpix (double *x, double *y, float *buffer, int Nx) {
+
+  int X, Y;
+  double Sx, Sy, So;
+
+  So = Sx = Sy = 0;
+  for (i = 0; i < Npix; i++) {
+    Y = pix / Nx;
+    X = pix % Nx;
+    So += buffer[pix];
+    Sx += X * buffer[pix];
+    Sy += Y * buffer[pix];
+  }
+  *x = Sx / So;
+  *y = Sy / So;
+}
+
+/* find stars: 
+   - binarize @ threshold
+   - find all contiguous blobs
+   - find geom center of each blob
+*/
+
+
+
+/*
+
+.....................
+....x................
+...xxx...............
+..........xxx..........
+.........xx..........
+.....................
+.....................
+.....................
+.....................
+.....................
+
+
+ */
+
 # endif
 
Index: /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/dimm/Makefile
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/dimm/Makefile	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/dimm/Makefile	(revision 23352)
@@ -26,7 +26,7 @@
 $(SRC)/init.$(ARCH).o		  	\
 $(SRC)/dimm.$(ARCH).o		  	\
-$(SRC)/camera.$(ARCH).o	  	\
+$(SRC)/camera_cmds.$(ARCH).o	  	\
 $(SRC)/findstars.$(ARCH).o	  	\
-$(SRC)/telescope.$(ARCH).o   \
+$(SRC)/telescope_cmds.$(ARCH).o   \
 $(SRC)/version.$(ARCH).o
 
@@ -64,6 +64,5 @@
 .PHONY: dimm
 
-# are these used or replaced?
-# $(SRC)/analysis.$(ARCH).o	  	\
+# these have not been finished: should be used for analysis of the extracted images
 # $(SRC)/Analysis.$(ARCH).o            \
 # $(SRC)/Image.$(ARCH).o		\
Index: anches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/dimm/analysis.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/dimm/analysis.c	(revision 23351)
+++ 	(revision )
@@ -1,184 +1,0 @@
-
-/* should this all be wrapped within an opihi implementation? */
-
-typedef struct {
-
-  /* image data area */
-  int Nx, Ny;
-  char *buffer;
-  int Nbytes;
-
-  /* image metadata */
-  double ccdtemp;
-  double airtemp;
-  double ra, dec, airmass;
-  double exptime;
-  int binning;
-} Image;
-
-subtractImage (Image *a, Image *b) {
-
-  if (a[0].Nx != b[0].Nx) return (FALSE);
-  if (a[0].Ny != b[0].Ny) return (FALSE);
-
-  Npix = a[0].Nx*a[0].Ny;
-  ap = (float *) a[0].buffer;
-  bp = (float *) b[0].buffer;
-  for (i = 0; i < Npix; i++, ap++, bp++) {
-    *ap -= *bp;
-  }
-  return (FALSE);
-}
-
-statsImage (Image *image, Stats *stats) {
-
-  val = (float *)image[0].buffer;
-  max = min = val[0];
-  Npix = image[0].Nx*image[0].Ny;
-  for (i = 0; i < Npix; i++, val++) {
-    N1 += *val;
-    N2 += (*val)*(*val);
-    max = MAX (max, *val);
-    min = MIN (min, *val);
-  }
-  stats[0].mean  = N1 / Npix;
-  stats[0].sigma = sqrt (N2 / Npix - SQ(stats[0].mean));
-  stats[0].min = min;
-  stats[0].max = max;
-
-  stats[0].median = stats[0].mean;
-  range = MAX (0.5, 0xffff / (max - min));
-  if (range == 0) return ();
-
-  ALLOCATE (hist, int, 0x10000);
-  bzero (hist, 0x10000*sizeof(int));
-
-  val = (float *)image[0].buffer;
-  for (i = 0; i < Npix; i++) {
-    bin = MIN (MAX (0, (*val - min) * range), 0xffff);
-    hist[bin] ++;
-  }
-
-  Nhist = 0;
-  for (i = 0; (i < 0xffff) && (Nhist < 0.5*Npix); i++) 
-    Nhist += hist[i];
-  stats[0].median = i / range + min;
-  free (hist);
-
-  return ();
-}
-
-findStars (Image *image, Stars **stars, int *Nstars, double threshold) {
-
-  /* binarize @ threshold */
-
-  binimage = createImage (image[0].Nx, image[0].Ny);
-
-  Npix = image[0].Nx*image[0].Ny;
-  ap = image[0].buffer;
-  bp = binimage[0].buffer;
-  bzero (bp, Npix*sizeof (short));
-
-  for (i = 0; i < Npix; i++, ap++, bp++) {
-    if (*ap > threshold) * bp = 1;
-  }
-
-  clearpix ();
-
-  for (j = 0; j < Ny; j++) {
-    for (i = 0; i < Nx; i++) {
-      if (binimage.buffer[j*Nx + i]) {
-	status = fillrow (binimage.buffer, Nx, j*Nx, i, &xs, &xe);
-	for (J = j + 1; (J < Ny) && status; J++) {
-	  for (I = xs; !binimage.buffer[J*Nx + I] && (I < xe); I++);
-	  if (binimage.buffer[J*Nx + I]) {
-	    status = fillrow (binimage.buffer, Nx, J*Nx, I, &xs, &xe);
-	  } 
-	}  
-	/* we now have a stack of pixels, convert to a single star */
-	statpix (&x, &y, image[0].buffer, Nx);
-	addstar (x, y);
-	clearpix ();
-      }
-    }
-  }
-}
-
-/* find contiguous trigger pixels in row from starting point */
-
-int fillrow (float *buffer, int Nx, int offset, int sx, int *xs, int *xe) {
-
-  trigger = FALSE;
-  for (i = sx, pix = offset + i; buffer[pix] && (i < Nx); i++, pix++) {
-    addpix (pix);
-    buffer[pix] = 0;
-    trigger = TRUE;
-    *xe = i;
-  }
-  for (i = sx - 1, pix = offset + i; (i >= 0) && buffer[pix]; i--, pix--) {
-    addpix (pix);
-    buffer[pix] = 0;
-    trigger = TRUE;
-    *xs = i;
-  }
-  return (trigger);
-}
-
-static int Npix = 0;
-static int *Pix = (int *) NULL;
-
-addpix (int pix) {
-  Npix ++;
-  if (Pix == (int *) NULL) {
-    ALLOCATE (Pix, int, MAX (1, Npix));
-  } else {
-    REALLOCATE (Pix, int, MAX (1, Npix));
-  }    
-  Pix[Npix - 1] = pix;
-}
-
-clearpix () {
-  Npix = 0;
-  REALLOCATE (Pix, int, 1);
-}
-
-statpix (double *x, double *y, float *buffer, int Nx) {
-
-  int X, Y;
-  double Sx, Sy, So;
-
-  So = Sx = Sy = 0;
-  for (i = 0; i < Npix; i++) {
-    Y = pix / Nx;
-    X = pix % Nx;
-    So += buffer[pix];
-    Sx += X * buffer[pix];
-    Sy += Y * buffer[pix];
-  }
-  *x = Sx / So;
-  *y = Sy / So;
-}
-
-/* find stars: 
-   - binarize @ threshold
-   - find all contiguous blobs
-   - find geom center of each blob
-*/
-
-
-
-/*
-
-.....................
-....x................
-...xxx...............
-..........xxx..........
-.........xx..........
-.....................
-.....................
-.....................
-.....................
-.....................
-
-
- */
Index: anches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/dimm/camera.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/dimm/camera.c	(revision 23351)
+++ 	(revision )
@@ -1,130 +1,0 @@
-# include "dimm.h"
-# define EXIT_STATUS(S) { seteuid (UID); return (S); }
-
-static uid_t UID, EUID;
-
-SetEUID () {
-
-  /* save the UID (ID of calling process) and EUID (should be root) */
-  UID = getuid ();
-  EUID = geteuid ();
-  seteuid (UID);
-}
-
-int camera (int argc, char **argv) {
-  
-  /* USAGE: 
-     camera init port
-     camera expose exptime
-     camera readout x y dx dy
-     camera temp set value
-     camera temp get var
-  */
-
-  if (argc < 2) goto usage;
-
-  seteuid (EUID);
-  
-  if (!strcasecmp (argv[1], "init")) {
-    int port, status;
-
-    if (argc != 3) {
-      gprint (GP_ERR, "USAGE: camera init (port)\n");
-      EXIT_STATUS (FALSE);
-    }
-    sscanf (argv[2], "%x", &port);
-    status = InitCamera (port);
-    EXIT_STATUS (status);
-  }
-
-  if (!strcasecmp (argv[1], "expose")) {
-
-    int status;
-    double exptime;
-
-    if (argc != 3) {
-      gprint (GP_ERR, "USAGE: camera expose (exptime)\n");
-      EXIT_STATUS (FALSE);
-    }
-    exptime = atof (argv[2]);
-    status = Exposure (exptime);
-    EXIT_STATUS (status);
-  }
-
-  if (!strcasecmp (argv[1], "temp")) {
-
-    int status;
-    double temp;
-
-    if (argc != 3) {
-      gprint (GP_ERR, "USAGE: camera temp (temperature)\n");
-      EXIT_STATUS (FALSE);
-    }
-    temp = atof (argv[2]);
-    status = SetTemperature (temp);
-    EXIT_STATUS (status);
-  }
-
-  if (!strcasecmp (argv[1], "status")) {
-
-    int status;
-    double temp;
-
-    if (argc != 2) {
-      gprint (GP_ERR, "USAGE: camera status\n");
-      EXIT_STATUS (FALSE);
-    }
-    DumpCameraStatus ();
-    EXIT_STATUS (TRUE);
-  }
-
-  if (!strcasecmp (argv[1], "readout")) {
-
-    int Nbuf, status;
-    double temp;
-    int x, y, dx, dy, NX, NY;
-    Buffer *buf;
-
-    if ((argc != 7) && (argc != 3)) {
-      gprint (GP_ERR, "USAGE: camera readout (buffer) x y dx dy\n");
-      EXIT_STATUS (FALSE);
-    }
-
-    if ((buf = SelectBuffer (argv[2], OLDBUFFER, TRUE)) == NULL) EXIT_STATUS (FALSE);
-
-    CameraFullSize (&NX, &NY);
-    x = y = 0;
-    dx = NX;
-    dy = NY;
-    if (argc == 7) {
-      x  = atof (argv[3]);
-      y  = atof (argv[4]);
-      dx = atof (argv[5]);
-      dy = atof (argv[6]);
-    } 
-
-    /* generate a buffer to store the image */
-    gfits_free_matrix (&buf[0].matrix);
-    gfits_free_header (&buf[0].header);
-    CreateBuffer (buf, dx, dy, -32, 0.0, 1.0);
-    strcpy (buf[0].file, "(empty)");
-
-    ReadOut (x, y, dx, dy, 1, buf[0].matrix.buffer);
-
-    gfits_convert_format (&buf[0].header, &buf[0].matrix, -32, 1.0, 0.0, 0xffff, gfits_get_unsign_mode());
-
-    EXIT_STATUS (TRUE);
-  }
-
-usage:
-  gprint (GP_ERR, "camera init port\n");
-  gprint (GP_ERR, "camera expose exptime\n");
-  gprint (GP_ERR, "camera readout x y dx dy\n");
-  gprint (GP_ERR, "camera temp set value\n");
-  gprint (GP_ERR, "camera temp get var\n");
-  seteuid (UID);
-  return (FALSE);
-
-}
-
-
Index: /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/dimm/camera_cmds.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/dimm/camera_cmds.c	(revision 23352)
+++ /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/dimm/camera_cmds.c	(revision 23352)
@@ -0,0 +1,130 @@
+# include "dimm.h"
+# define EXIT_STATUS(S) { seteuid (UID); return (S); }
+
+static uid_t UID, EUID;
+
+SetEUID () {
+
+  /* save the UID (ID of calling process) and EUID (should be root) */
+  UID = getuid ();
+  EUID = geteuid ();
+  seteuid (UID);
+}
+
+int camera (int argc, char **argv) {
+  
+  /* USAGE: 
+     camera init port
+     camera expose exptime
+     camera readout x y dx dy
+     camera temp set value
+     camera temp get var
+  */
+
+  if (argc < 2) goto usage;
+
+  seteuid (EUID);
+  
+  if (!strcasecmp (argv[1], "init")) {
+    int port, status;
+
+    if (argc != 3) {
+      gprint (GP_ERR, "USAGE: camera init (port)\n");
+      EXIT_STATUS (FALSE);
+    }
+    sscanf (argv[2], "%x", &port);
+    status = InitCamera (port);
+    EXIT_STATUS (status);
+  }
+
+  if (!strcasecmp (argv[1], "expose")) {
+
+    int status;
+    double exptime;
+
+    if (argc != 3) {
+      gprint (GP_ERR, "USAGE: camera expose (exptime)\n");
+      EXIT_STATUS (FALSE);
+    }
+    exptime = atof (argv[2]);
+    status = Exposure (exptime);
+    EXIT_STATUS (status);
+  }
+
+  if (!strcasecmp (argv[1], "temp")) {
+
+    int status;
+    double temp;
+
+    if (argc != 3) {
+      gprint (GP_ERR, "USAGE: camera temp (temperature)\n");
+      EXIT_STATUS (FALSE);
+    }
+    temp = atof (argv[2]);
+    status = SetTemperature (temp);
+    EXIT_STATUS (status);
+  }
+
+  if (!strcasecmp (argv[1], "status")) {
+
+    int status;
+    double temp;
+
+    if (argc != 2) {
+      gprint (GP_ERR, "USAGE: camera status\n");
+      EXIT_STATUS (FALSE);
+    }
+    DumpCameraStatus ();
+    EXIT_STATUS (TRUE);
+  }
+
+  if (!strcasecmp (argv[1], "readout")) {
+
+    int Nbuf, status;
+    double temp;
+    int x, y, dx, dy, NX, NY;
+    Buffer *buf;
+
+    if ((argc != 7) && (argc != 3)) {
+      gprint (GP_ERR, "USAGE: camera readout (buffer) x y dx dy\n");
+      EXIT_STATUS (FALSE);
+    }
+
+    if ((buf = SelectBuffer (argv[2], OLDBUFFER, TRUE)) == NULL) EXIT_STATUS (FALSE);
+
+    CameraFullSize (&NX, &NY);
+    x = y = 0;
+    dx = NX;
+    dy = NY;
+    if (argc == 7) {
+      x  = atof (argv[3]);
+      y  = atof (argv[4]);
+      dx = atof (argv[5]);
+      dy = atof (argv[6]);
+    } 
+
+    /* generate a buffer to store the image */
+    gfits_free_matrix (&buf[0].matrix);
+    gfits_free_header (&buf[0].header);
+    CreateBuffer (buf, dx, dy, -32, 0.0, 1.0);
+    strcpy (buf[0].file, "(empty)");
+
+    ReadOut (x, y, dx, dy, 1, buf[0].matrix.buffer);
+
+    gfits_convert_format (&buf[0].header, &buf[0].matrix, -32, 1.0, 0.0, 0xffff, gfits_get_unsign_mode());
+
+    EXIT_STATUS (TRUE);
+  }
+
+usage:
+  gprint (GP_ERR, "camera init port\n");
+  gprint (GP_ERR, "camera expose exptime\n");
+  gprint (GP_ERR, "camera readout x y dx dy\n");
+  gprint (GP_ERR, "camera temp set value\n");
+  gprint (GP_ERR, "camera temp get var\n");
+  seteuid (UID);
+  return (FALSE);
+
+}
+
+
Index: anches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/dimm/telescope.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/dimm/telescope.c	(revision 23351)
+++ 	(revision )
@@ -1,234 +1,0 @@
-# include "dimm.h"
-
-double distSky (double r1, double r2, double d1, double d2);
-
-int telescope (int argc, char **argv) {
-  
-  if (argc < 2) goto usage;
-
-  if (!strcasecmp (argv[1], "init")) {
-    if (argc != 3) {
-      gprint (GP_ERR, "USAGE: telescope init (port)\n");
-      return (FALSE);
-    }
-    if (!SerialInit (argv[2])) return (FALSE);
-    gprint (GP_ERR, "telescope on port %s\n", argv[2]);
-    return (TRUE);
-  }
-
-  if (!strcasecmp (argv[1], "cmd")) {
-
-    int status;
-    char *answer = (char *) NULL;
-
-    if (argc != 3) {
-      gprint (GP_ERR, "USAGE: telescope cmd (string)\n");
-      return (FALSE);
-    }
-    status = SerialCommand (argv[2], &answer, 10);
-    gprint (GP_ERR, "status: %d\n", status);
-    if (answer != (char *) NULL) {
-      gprint (GP_ERR, "answer: ..%s..\n", answer);
-    }
-    return (TRUE);
-  }
-
-  if (!strcasecmp (argv[1], "ack")) {
-
-    int status;
-    char line[32], *answer;
-
-    if (argc != 3) {
-      gprint (GP_ERR, "USAGE: telescope cmd (string)\n");
-      return (FALSE);
-    }
-    line[0] = 0x06;
-    line[1] = 0;
-    status = SerialCommand (line, &answer, 10);
-    gprint (GP_ERR, "status: %d\n", status);
-    if (answer != (char *) NULL) {
-      gprint (GP_ERR, "answer: ..%s..\n", answer);
-    }
-    return (TRUE);
-  }
-
-  if (!strcasecmp (argv[1], "slew")) {
-
-    double ra, dec;
-    int status;
-
-    if (argc != 4) {
-      gprint (GP_ERR, "USAGE: telescope slew (ra) (dec)\n");
-      return (FALSE);
-    }
-
-    ra  = ohana_normalize_angle(atof (argv[2]));
-    dec = atof (argv[3]);
-
-    status = gotoRD (ra, dec);
-
-    if (!status) return (FALSE);
-    return (TRUE);
-  }
-
-  if (!strcasecmp (argv[1], "coords")) {
-
-    int status;
-    double ra, dec;
-    char line[64];
-
-    if (argc != 2) {
-      gprint (GP_ERR, "USAGE: telescope coords\n");
-      return (FALSE);
-    }
-
-    if (!getRD (&ra, &dec)) return (FALSE);
-    gprint (GP_ERR, "%f %f\n", ra, dec);
-    set_variable ("RA", ra);
-    set_variable ("DEC", dec);
-    dms_format (line, (ra/15.0));
-    set_str_variable ("Rs", line);
-    dms_format (line, dec);
-    set_str_variable ("Ds", line);
-
-    return (TRUE);
-  }
-
-  if (!strcasecmp (argv[1], "altaz")) {
-
-    int status;
-    double x, y;
-
-    if (argc != 2) {
-      gprint (GP_ERR, "USAGE: telescope altaz\n");
-      return (FALSE);
-    }
-
-    if (!getXY (&x, &y)) return (FALSE);
-    gprint (GP_ERR, "%f %f\n", x, y);
-    set_variable ("ALT", x);
-    set_variable ("AZ", y);
-    return (TRUE);
-  }
-
-  if (!strcasecmp (argv[1], "site")) {
-
-    int status;
-    double lon, lat, LST;
-
-    if (argc != 2) {
-      gprint (GP_ERR, "USAGE: telescope site\n");
-      return (FALSE);
-    }
-
-    if (!getSite (&lon, &lat, &LST)) return (FALSE);
-    gprint (GP_ERR, "%f %f  %f\n", lon, lat, LST);
-    set_variable ("LON", lon);
-    set_variable ("LAT", lat);
-    set_variable ("LST", LST);
-    return (TRUE);
-  }
-
-  if (!strcasecmp (argv[1], "setsite")) {
-
-    double lon, lat;
-
-    if (argc != 5) {
-      gprint (GP_ERR, "USAGE: telescope setsite (name) (longitude) (latitude)\n");
-      return (FALSE);
-    }
-
-    lon = atof (argv[3]);
-    lat = atof (argv[4]);
-    if (!setSite (argv[2], lon, lat)) return (FALSE);
-    set_variable ("LON", lon);
-    set_variable ("LAT", lat);
-    return (TRUE);
-  }
-
-  if (!strcasecmp (argv[1], "settime")) {
-
-    if (argc != 3) {
-      gprint (GP_ERR, "USAGE: telescope settime (lst)\n");
-      return (FALSE);
-    }
-
-    if (!setTime (argv[2])) return (FALSE);
-    return (TRUE);
-  }
-
-  if (!strcasecmp (argv[1], "verbose")) {
-
-    int mode;
-
-    if (argc != 3) {
-      gprint (GP_ERR, "USAGE: telescope verbose (mode)\n");
-      return (FALSE);
-    }
-
-    mode = atoi (argv[2]);
-    SerialVerbose (mode);
-    return (TRUE);
-  }
-
-  if (!strcasecmp (argv[1], "setcoords")) {
-
-    int status;
-    double ra, dec;
-
-    if (argc != 4) {
-      gprint (GP_ERR, "USAGE: telescope setcoords (ra) (dec)\n");
-      return (FALSE);
-    }
-
-    ra  = atof (argv[2]);
-    dec = atof (argv[3]);
-
-    if (!setRD (ra, dec)) return (FALSE);
-    return (TRUE);
-  }
-
-  if (!strcasecmp (argv[1], "offset")) {
-
-    int status;
-
-    if (argc != 4) {
-      gprint (GP_ERR, "USAGE: telescope offset (direction) (distance)\n");
-      gprint (GP_ERR, "  direction : x or y\n");
-      gprint (GP_ERR, "  distance  : +arcmin or -arcmin\n");
-      return (FALSE);
-    }
-    status = offset (argv[2], atof (argv[3]));
-
-    if (!status) return (FALSE);
-    return (TRUE);
-  }
-
-  if (!strcasecmp (argv[1], "toffset")) {
-
-    int status;
-
-    if (argc != 5) {
-      gprint (GP_ERR, "USAGE: telescope toffset (direction) (rate) (duration)\n");
-      gprint (GP_ERR, "example: telescope toffset x RC 1.5\n");
-      return (FALSE);
-    }
-    status = toffset (argv[2], argv[3], atof(argv[4]));
-
-    if (!status) return (FALSE);
-    return (TRUE);
-  }
-
- usage:
-  gprint (GP_ERR, "telescope init port - set serial port (eg, /dev/ttyS0)\n");
-  gprint (GP_ERR, "telescope site - get site information\n");
-  gprint (GP_ERR, "telescope altaz - g\n");
-  gprint (GP_ERR, "telescope setcoords (ra) (dec)\n");
-  gprint (GP_ERR, "telescope setsite (name) (longitute) (latitude)\n");
-  gprint (GP_ERR, "telescope coords\n");
-  gprint (GP_ERR, "telescope slew (ra) (dec)\n");
-  gprint (GP_ERR, "telescope offset (direction) (duration)\n");
-  gprint (GP_ERR, "telescope toffset (direction) (rate) (duration)\n");
-  return (FALSE);
-}
-
Index: /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/dimm/telescope_cmds.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/dimm/telescope_cmds.c	(revision 23352)
+++ /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/dimm/telescope_cmds.c	(revision 23352)
@@ -0,0 +1,234 @@
+# include "dimm.h"
+
+double distSky (double r1, double r2, double d1, double d2);
+
+int telescope (int argc, char **argv) {
+  
+  if (argc < 2) goto usage;
+
+  if (!strcasecmp (argv[1], "init")) {
+    if (argc != 3) {
+      gprint (GP_ERR, "USAGE: telescope init (port)\n");
+      return (FALSE);
+    }
+    if (!SerialInit (argv[2])) return (FALSE);
+    gprint (GP_ERR, "telescope on port %s\n", argv[2]);
+    return (TRUE);
+  }
+
+  if (!strcasecmp (argv[1], "cmd")) {
+
+    int status;
+    char *answer = (char *) NULL;
+
+    if (argc != 3) {
+      gprint (GP_ERR, "USAGE: telescope cmd (string)\n");
+      return (FALSE);
+    }
+    status = SerialCommand (argv[2], &answer, 10);
+    gprint (GP_ERR, "status: %d\n", status);
+    if (answer != (char *) NULL) {
+      gprint (GP_ERR, "answer: ..%s..\n", answer);
+    }
+    return (TRUE);
+  }
+
+  if (!strcasecmp (argv[1], "ack")) {
+
+    int status;
+    char line[32], *answer;
+
+    if (argc != 3) {
+      gprint (GP_ERR, "USAGE: telescope cmd (string)\n");
+      return (FALSE);
+    }
+    line[0] = 0x06;
+    line[1] = 0;
+    status = SerialCommand (line, &answer, 10);
+    gprint (GP_ERR, "status: %d\n", status);
+    if (answer != (char *) NULL) {
+      gprint (GP_ERR, "answer: ..%s..\n", answer);
+    }
+    return (TRUE);
+  }
+
+  if (!strcasecmp (argv[1], "slew")) {
+
+    double ra, dec;
+    int status;
+
+    if (argc != 4) {
+      gprint (GP_ERR, "USAGE: telescope slew (ra) (dec)\n");
+      return (FALSE);
+    }
+
+    ra  = ohana_normalize_angle(atof (argv[2]));
+    dec = atof (argv[3]);
+
+    status = gotoRD (ra, dec);
+
+    if (!status) return (FALSE);
+    return (TRUE);
+  }
+
+  if (!strcasecmp (argv[1], "coords")) {
+
+    int status;
+    double ra, dec;
+    char line[64];
+
+    if (argc != 2) {
+      gprint (GP_ERR, "USAGE: telescope coords\n");
+      return (FALSE);
+    }
+
+    if (!getRD (&ra, &dec)) return (FALSE);
+    gprint (GP_ERR, "%f %f\n", ra, dec);
+    set_variable ("RA", ra);
+    set_variable ("DEC", dec);
+    dms_format (line, (ra/15.0));
+    set_str_variable ("Rs", line);
+    dms_format (line, dec);
+    set_str_variable ("Ds", line);
+
+    return (TRUE);
+  }
+
+  if (!strcasecmp (argv[1], "altaz")) {
+
+    int status;
+    double x, y;
+
+    if (argc != 2) {
+      gprint (GP_ERR, "USAGE: telescope altaz\n");
+      return (FALSE);
+    }
+
+    if (!getXY (&x, &y)) return (FALSE);
+    gprint (GP_ERR, "%f %f\n", x, y);
+    set_variable ("ALT", x);
+    set_variable ("AZ", y);
+    return (TRUE);
+  }
+
+  if (!strcasecmp (argv[1], "site")) {
+
+    int status;
+    double lon, lat, LST;
+
+    if (argc != 2) {
+      gprint (GP_ERR, "USAGE: telescope site\n");
+      return (FALSE);
+    }
+
+    if (!getSite (&lon, &lat, &LST)) return (FALSE);
+    gprint (GP_ERR, "%f %f  %f\n", lon, lat, LST);
+    set_variable ("LON", lon);
+    set_variable ("LAT", lat);
+    set_variable ("LST", LST);
+    return (TRUE);
+  }
+
+  if (!strcasecmp (argv[1], "setsite")) {
+
+    double lon, lat;
+
+    if (argc != 5) {
+      gprint (GP_ERR, "USAGE: telescope setsite (name) (longitude) (latitude)\n");
+      return (FALSE);
+    }
+
+    lon = atof (argv[3]);
+    lat = atof (argv[4]);
+    if (!setSite (argv[2], lon, lat)) return (FALSE);
+    set_variable ("LON", lon);
+    set_variable ("LAT", lat);
+    return (TRUE);
+  }
+
+  if (!strcasecmp (argv[1], "settime")) {
+
+    if (argc != 3) {
+      gprint (GP_ERR, "USAGE: telescope settime (lst)\n");
+      return (FALSE);
+    }
+
+    if (!setTime (argv[2])) return (FALSE);
+    return (TRUE);
+  }
+
+  if (!strcasecmp (argv[1], "verbose")) {
+
+    int mode;
+
+    if (argc != 3) {
+      gprint (GP_ERR, "USAGE: telescope verbose (mode)\n");
+      return (FALSE);
+    }
+
+    mode = atoi (argv[2]);
+    SerialVerbose (mode);
+    return (TRUE);
+  }
+
+  if (!strcasecmp (argv[1], "setcoords")) {
+
+    int status;
+    double ra, dec;
+
+    if (argc != 4) {
+      gprint (GP_ERR, "USAGE: telescope setcoords (ra) (dec)\n");
+      return (FALSE);
+    }
+
+    ra  = atof (argv[2]);
+    dec = atof (argv[3]);
+
+    if (!setRD (ra, dec)) return (FALSE);
+    return (TRUE);
+  }
+
+  if (!strcasecmp (argv[1], "offset")) {
+
+    int status;
+
+    if (argc != 4) {
+      gprint (GP_ERR, "USAGE: telescope offset (direction) (distance)\n");
+      gprint (GP_ERR, "  direction : x or y\n");
+      gprint (GP_ERR, "  distance  : +arcmin or -arcmin\n");
+      return (FALSE);
+    }
+    status = offset (argv[2], atof (argv[3]));
+
+    if (!status) return (FALSE);
+    return (TRUE);
+  }
+
+  if (!strcasecmp (argv[1], "toffset")) {
+
+    int status;
+
+    if (argc != 5) {
+      gprint (GP_ERR, "USAGE: telescope toffset (direction) (rate) (duration)\n");
+      gprint (GP_ERR, "example: telescope toffset x RC 1.5\n");
+      return (FALSE);
+    }
+    status = toffset (argv[2], argv[3], atof(argv[4]));
+
+    if (!status) return (FALSE);
+    return (TRUE);
+  }
+
+ usage:
+  gprint (GP_ERR, "telescope init port - set serial port (eg, /dev/ttyS0)\n");
+  gprint (GP_ERR, "telescope site - get site information\n");
+  gprint (GP_ERR, "telescope altaz - g\n");
+  gprint (GP_ERR, "telescope setcoords (ra) (dec)\n");
+  gprint (GP_ERR, "telescope setsite (name) (longitute) (latitude)\n");
+  gprint (GP_ERR, "telescope coords\n");
+  gprint (GP_ERR, "telescope slew (ra) (dec)\n");
+  gprint (GP_ERR, "telescope offset (direction) (duration)\n");
+  gprint (GP_ERR, "telescope toffset (direction) (rate) (duration)\n");
+  return (FALSE);
+}
+
Index: /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/CheckTasks.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/CheckTasks.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/CheckTasks.c	(revision 23352)
@@ -48,5 +48,5 @@
     // add random offset between 0 and 5% of exec_period
     // XXX this should be optional
-    fuzz = 0.05*task[0].exec_period*drand48() + 1e-6*task[0].last.tv_usec; 
+    fuzz = 0.1*task[0].exec_period*drand48();
     task[0].last.tv_usec = 1e6*(fuzz - (int)fuzz);
     task[0].last.tv_sec += (int) fuzz;
Index: /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/task_threads.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/task_threads.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/task_threads.c	(revision 23352)
@@ -2,8 +2,12 @@
 
 /** things related to CheckTasks **/
+void ResetTaskTimers ();
 
 static int CheckTasksRun = FALSE;
 
 void CheckTasksSetState (int state) {
+  if (state && !CheckTasksRun) {
+    ResetTaskTimers ();
+  }
   CheckTasksRun = state;
 }
@@ -51,2 +55,29 @@
 // timeout.  this enforces a certain granularity in the task creation, but prevents the task
 // thread from driving the load up to silly levels.
+
+// reset all of the task timers, with a bit of fuzz.  this is called 
+// whenever we transition from 'stop' to 'run'
+void ResetTaskTimers () {
+
+  Task *task;
+  struct timeval now;
+  float fuzz;
+
+  // get the current time
+  gettimeofday (&now, NULL);
+
+  // check all tasks
+  while ((task = NextTask ()) != NULL) {
+    task[0].last.tv_usec = now.tv_usec;
+    task[0].last.tv_sec  = now.tv_sec;
+
+    // add random offset between 0 and 10% of exec_period
+    // XXX this should be optional
+    fuzz = task[0].exec_period*drand48();
+    task[0].last.tv_usec += 1e6*(fuzz - (int)fuzz);
+    task[0].last.tv_sec += (int) fuzz;
+
+    // gprint (GP_LOG, "fuzz: %f, last: %d %d\n", fuzz, task[0].last.tv_sec, task[0].last.tv_usec);
+  }
+  return;
+}
Index: /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pcontrol/CheckIdleHost.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pcontrol/CheckIdleHost.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pcontrol/CheckIdleHost.c	(revision 23352)
@@ -40,5 +40,5 @@
     host[0].job = (struct Job *) job;
 
-    if (logfile) fprintf (logfile, "start needhost %s (job host %s) : %s\n", host[0].hostname, job[0].hostname, job[0].argv[0]);
+    // if (logfile) fprintf (logfile, "start needhost %s (job host %s) : %s\n", host[0].hostname, job[0].hostname, job[0].argv[0]);
 
     /* take the job off the stack and unlock the stack */
@@ -60,5 +60,5 @@
     host[0].job = (struct Job *) job;
 
-    if (logfile) fprintf (logfile, "start wanthost %s (job host %s) : %s\n", host[0].hostname, job[0].hostname, job[0].argv[0]);
+    // if (logfile) fprintf (logfile, "start wanthost %s (job host %s) : %s\n", host[0].hostname, job[0].hostname, job[0].argv[0]);
 
     /* take the job off the stack and unlock the stack */
@@ -78,5 +78,5 @@
     host[0].job = (struct Job *) job;
 
-    if (logfile) fprintf (logfile, "start  anyhost %s (job host %s) : %s\n", host[0].hostname, job[0].hostname, job[0].argv[0]);
+    // if (logfile) fprintf (logfile, "start  anyhost %s (job host %s) : %s\n", host[0].hostname, job[0].hostname, job[0].argv[0]);
 
     /* take the job off the stack and unlock the stack */
Index: /branches/cnb_branches/cnb_branch_20090301/PS-IPP-Config/lib/PS/IPP/Config.pm
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/PS-IPP-Config/lib/PS/IPP/Config.pm	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/PS-IPP-Config/lib/PS/IPP/Config.pm	(revision 23352)
@@ -71,5 +71,5 @@
     unless (defined $class) {
         carp "Programming error";
-        exit($PS_EXIT_PROG_ERROR);
+        return undef;
     }
 
@@ -85,5 +85,5 @@
     unless (open $file, $name) {
         carp "Unable to open ipprc file $name: $!";
-        exit($PS_EXIT_CONFIG_ERROR);
+        return undef;
     }
     my @contents = <$file>;        # Contents of the ipprc file
@@ -95,5 +95,5 @@
     unless (defined $path) {
         carp "PATH is not set in $name\n";
-        exit($PS_EXIT_CONFIG_ERROR);
+        return undef;
     }
 
@@ -112,8 +112,8 @@
     bless $self, $class;
 
-    $self->load_site();
-    $self->load_system();
-
-    $self->define_camera($camera) if defined $camera;
+    $self->load_site() or return undef;
+    $self->load_system() or return undef;
+
+    ( $self->define_camera($camera) or return undef ) if defined $camera;
 
     return $self;
@@ -148,5 +148,5 @@
                 unless ($found) {
                     carp "Unable to find camera configuration file $filename\n";
-                    exit($PS_EXIT_CONFIG_ERROR);
+                    return undef;
                 }
             }
@@ -158,5 +158,5 @@
 
     carp "Unable to find configuration file: $filename";
-    exit($PS_EXIT_CONFIG_ERROR);
+    return undef;
 }
 
@@ -170,5 +170,5 @@
     unless (defined $self) {
         carp "Programming error";
-        exit($PS_EXIT_PROG_ERROR);
+        return undef;
     }
 
@@ -176,8 +176,9 @@
     unless (defined $filename) {
         carp "Unable to find site configuration file\n";
-        exit($PS_EXIT_CONFIG_ERROR);
+        return undef;
     }
 
     my $realfile = $self->_find_config($filename); # Resolved filename, after hunting the PATH
+    ( carp "Unable to find site configuration" and return undef ) unless defined $realfile;
 
     # Read the file
@@ -185,5 +186,5 @@
     unless (open $file, $realfile) {
         carp "Unable to open site configuration file $realfile: $!";
-        exit($PS_EXIT_CONFIG_ERROR);
+        return undef;
     }
 
@@ -194,5 +195,5 @@
     unless (defined $self->{_siteConfig}) {
         carp "Failure to parse the site configuration file $realfile";
-        exit($PS_EXIT_CONFIG_ERROR);
+        return undef;
     }
 
@@ -207,5 +208,5 @@
     unless (defined $self) {
         carp "Programming error";
-        exit($PS_EXIT_PROG_ERROR);
+        return undef;
     }
 
@@ -213,8 +214,9 @@
     unless (defined $filename) {
         carp "Unable to find system configuration file\n";
-        exit($PS_EXIT_CONFIG_ERROR);
+        return undef;
     }
 
     my $realfile = $self->_find_config($filename); # Resolved filename, after hunting the PATH
+    ( carp "Unable to find system configuration" and return undef ) unless defined $realfile;
 
     # Read the file
@@ -222,5 +224,5 @@
     unless (open $file, $realfile) {
         carp "Unable to open system configuration file $realfile: $!";
-        exit($PS_EXIT_CONFIG_ERROR);
+        return undef;
     }
     my @contents = <$file>;
@@ -230,5 +232,5 @@
     unless (defined $self->{_systemConfig}) {
         carp "Failure to define system configuration";
-        exit($PS_EXIT_CONFIG_ERROR);
+        return undef;
     }
 
@@ -244,5 +246,5 @@
     unless (defined $self and defined $name) {
         carp "Programming error";
-        exit($PS_EXIT_PROG_ERROR);
+        return undef;
     }
 
@@ -251,8 +253,9 @@
     unless (defined $filename) {
         carp "Unable to find configuration file for camera $name\n";
-        exit($PS_EXIT_CONFIG_ERROR);
+        return undef;
     }
 
     my $realfile = $self->_find_config($filename); # Resolved filename, after hunting the PATH
+    ( carp "Unable to find camera configuration" and return undef ) unless defined $realfile;
 
     # Read the file
@@ -260,5 +263,5 @@
     unless (open $file, $realfile) {
         carp "Unable to open camera configuration file $realfile: $!";
-        exit($PS_EXIT_CONFIG_ERROR);
+        return undef;
     }
     my @contents = <$file>;
@@ -269,5 +272,5 @@
     unless (defined $self->{camera}) {
         carp "Failure to define camera";
-        exit($PS_EXIT_CONFIG_ERROR);
+        return undef;
     }
 
@@ -326,5 +329,5 @@
     unless (defined $self and defined $name) {
         carp "Programming error";
-        exit($PS_EXIT_PROG_ERROR);
+        return undef;
     }
 
@@ -333,5 +336,5 @@
     unless (defined $pathname) {
         carp "Unable to find datapath $name\n" ;
-        exit($PS_EXIT_CONFIG_ERROR);
+        return undef;
     }
 
@@ -340,5 +343,5 @@
 
 # convert the database name and the table ID to a image source id
-sub source_id 
+sub source_id
 {
     my $self = shift;
@@ -351,8 +354,8 @@
     my $admindb = "ippadmin";
 
-    die "dbserver not defined in configuration" unless defined($dbserver);
-    die "dbuser not defined in configuration" unless defined($dbuser);
-    die "dbpassword not defined in configuration" unless defined($dbpassword);
-    die "dbname not defined in configuration" unless defined($dbname);
+    ( carp "dbserver not defined in configuration"  and return undef ) unless defined($dbserver);
+    ( carp "dbuser not defined in configuration"  and return undef ) unless defined($dbuser);
+    ( carp "dbpassword not defined in configuration"  and return undef ) unless defined($dbpassword);
+    ( carp "dbname not defined in configuration"  and return undef ) unless defined($dbname);
 
     my $dsn = "DBI:mysql:host=$dbserver;database=$admindb";
@@ -363,5 +366,5 @@
     $stmt->execute();
     my $ref = $stmt->fetchrow_hashref();
-    die "ippdb $dbname not found" unless ($ref);
+    ( carp "ippdb $dbname not found" and return undef ) unless ($ref);
 
     my $proj_id = $ref->{proj_id};
@@ -382,5 +385,5 @@
     if ($@) {
         carp "Can't find Nebulous::Client.";
-        exit($PS_EXIT_PROG_ERROR);
+        return undef;
     }
 
@@ -390,11 +393,11 @@
     unless (defined $server) {
         carp "Unable to find NEB_SERVER in camera configuration file.";
-        exit($PS_EXIT_CONFIG_ERROR);
-    }
-
-    my $neb = Nebulous::Client->new( proxy => $server );
-    unless (defined $neb) {
+        return undef;
+    }
+
+    my $neb = eval { Nebulous::Client->new( proxy => $server ); };
+    if ($@ or not defined $neb) {
         carp "Unable to find NEB_SERVER in camera configuration file.";
-        exit($PS_EXIT_CONFIG_ERROR);
+        return undef;
     }
 
@@ -415,28 +418,30 @@
     if ($scheme) {
         $scheme = lc($scheme);
-	# print "scheme: $scheme\n";
+        # print "scheme: $scheme\n";
 
         if ($scheme eq 'neb') {
-            $self->_neb_start();
+            $self->_neb_start() or ( carp "Can't start Nebulous" and return undef );
             my $neb = $self->{nebulous}; # Nebulous handle
             if ($create_if_doesnt_exist) {
-                unless ($neb->stat( $name )) {
-		    # print "entry $name not found, creating...\n";
-                    my $uri = $neb->create( $name );
-                    unless(defined $uri) {
+                my $status = eval { $neb->stat( $name ); };
+                ( carp "Unable to stat Nebulous handle $name" and return undef ) if $@;
+                unless ($status) {
+                    # print "entry $name not found, creating...\n";
+                    my $uri = eval { $neb->create( $name ); };
+                    if ($@ or not defined $uri) {
                         carp "unable to instantiate $name.";
-                        exit($PS_EXIT_DATA_ERROR);
+                        return undef;
                     }
                     my $path = URI->new( $uri )->path;
-		    # print "created path: $path\n";
+                    # print "created path: $path\n";
                     return $path;
                 }
             }
-	    my $path = $neb->find( $name );
-	    if (not defined $path) { 
-		carp "neb entry $name not found, not created\n"; 
-		exit($PS_EXIT_DATA_ERROR);
-	    } 
-	    # print "found path: $path\n";
+            my $path = eval { $neb->find( $name ); };
+            if ($@ or not defined $path) {
+                carp "neb entry $name not found, not created\n";
+                return undef;
+            }
+            # print "found path: $path\n";
             return $path;
         }
@@ -444,6 +449,6 @@
         if ($scheme eq 'path' or $scheme eq 'file') {
             # guaranteed to have a scheme (path:// or file://)
-            $name = $self->convert_filename_absolute( $name );
-	    # print "resolved path to $name\n";
+            $name = $self->convert_filename_absolute( $name ) or return undef;
+            # print "resolved path to $name\n";
         }
     }
@@ -454,12 +459,13 @@
         if (! -e $dir) {
             my $rc = system "mkdir -p $dir";
-            die "failed to create directory for $name" unless (!$rc);
+            ( carp "failed to create directory for $name" and return undef ) unless (!$rc);
         } elsif (! -d $dir ) {
-            die "parent for $name exists and is not a directory";
-        }
-
-        open F, ">$name" or die "failed to create $name";
+            carp "parent for $name exists and is not a directory";
+            return undef;
+        }
+
+        open F, ">$name" or ( carp "failed to create $name" and return undef );
         close F;
-	# print "created target $name\n";
+        # print "created target $name\n";
     }
 
@@ -473,5 +479,5 @@
     my $name = shift;                # File name to check
 
-    $self->file_prepare( $name );
+    $self->file_prepare( $name ) or return undef;
 
     my $scheme = file_scheme($name); # The scheme, e.g., file://, path://
@@ -479,10 +485,15 @@
         $scheme = lc($scheme);
         if ($scheme eq 'neb') {
-            $self->_neb_start();
-            return $self->{nebulous}->open_create( $name );
+            $self->_neb_start() or ( carp "Unable to start Nebulous" and return undef );
+            my $fh = eval { $self->{nebulous}->open_create( $name ); };
+            if ($@ or not defined $fh) {
+                carp "Unable to open/create Nebulous handle $name";
+                return undef;
+            }
+            return $fh;
         }
         if ($scheme eq 'path' or $scheme eq 'file') {
             # guaranteed to have a scheme (path:// or file://)
-            $name = $self->convert_filename_absolute( $name );
+            $name = $self->convert_filename_absolute( $name ) or return undef;
         }
     }
@@ -490,5 +501,5 @@
     if (-f $name) {
         carp "Unable to create file $name --- file exists.";
-        exit($PS_EXIT_SYS_ERROR);
+        return undef;
     }
 
@@ -496,5 +507,5 @@
     unless (open $fh, '>', $name) {
         carp "Unable to create file $name --- $!";
-        exit($PS_EXIT_SYS_ERROR);
+        return undef;
     }
     return $fh;
@@ -507,5 +518,5 @@
     my $name = shift;                # File name to check
 
-    $self->file_prepare( $name );
+    $self->file_prepare( $name ) or return undef;
 
     my $scheme = file_scheme($name); # The scheme, e.g., file://, path://
@@ -513,10 +524,15 @@
         $scheme = lc($scheme);
         if ($scheme eq 'neb') {
-            $self->_neb_start();
-            return $self->{nebulous}->open_create( $name );
+            $self->_neb_start() or ( carp "Unable to start Nebulous" and return undef );
+            my $fh = eval { $self->{nebulous}->open_create( $name ) };
+            if ($@ or not defined $fh) {
+                carp "Unable to open/create Nebulous handle $name";
+                return undef;
+            }
+            return $fh;
         }
         if ($scheme eq 'path' or $scheme eq 'file') {
             # guaranteed to have a scheme (path:// or file://)
-            $name = $self->convert_filename_absolute( $name );
+            $name = $self->convert_filename_absolute( $name ) or return undef;
         }
     }
@@ -525,5 +541,5 @@
     unless (open $fh, '>>', $name) {
         carp "Unable to create file $name --- $!";
-        exit($PS_EXIT_SYS_ERROR);
+        return undef;
     }
     return $fh;
@@ -536,10 +552,14 @@
     my $name = shift;                # File name to check
 
-    $self->file_prepare( $name );
+    $self->file_prepare( $name ) or return undef;
 
     my $scheme = file_scheme($name); # The scheme, e.g., file://, path://
     if (defined $scheme and lc($scheme) eq 'neb') {
-        $self->_neb_start();
-        $name = $self->{nebulous}->create( $name );
+        $self->_neb_start() or ( carp "Unable to start Nebulous" and return undef );
+        $name = eval { $self->{nebulous}->create( $name ) };
+        if ($@ or not defined $name) {
+            carp "Unable to create Nebulous handle $name";
+            return undef;
+        }
     }
 
@@ -555,6 +575,8 @@
     my $scheme = file_scheme($name); # The scheme, e.g., file://, path://
     if (defined $scheme and lc($scheme) eq 'neb') {
-        $self->_neb_start();
-        return (defined $self->{nebulous}->find_instances( $name ) ? 1 : 0);
+        $self->_neb_start() or ( carp "Unable to start Nebulous" and return undef );
+        my $found = eval { $self->{nebulous}->find_instances( $name ); };
+        ( carp "Unable to find instances of Nebulous handle $name" and return undef ) if $@;
+        return (defined $found ? 1 : 0);
     }
 
@@ -569,16 +591,19 @@
     my $target = shift;                # Name of target file
 
-    $self->file_prepare( $target );
+    $self->file_prepare( $target ) or return undef;
 
     my $scheme = file_scheme($target); # The scheme, e.g., file://, path://
     if (defined $scheme and lc($scheme) eq 'neb') {
-        $self->_neb_start();
-        $target = $self->{nebulous}->create( $target );
+        $self->_neb_start() or ( carp "Unable to start Nebulous" and return undef );
+        $target = eval { $self->{nebulous}->create( $target ); };
+        if ($@ or not defined $target) {
+            carp "Unable to create Nebulous handle";
+            return undef;
+        }
     }
     $target = $self->file_resolve( $target );
     $source = $self->file_resolve( $source );
 
-    system("cp $source $target") == 0 or (carp "Can't copy file $source to $target." and
-                                          exit($PS_EXIT_DATA_ERROR));
+    system("cp $source $target") == 0 or ( carp "Can't copy file $source to $target." and return undef );
     return 1;
 }
@@ -593,5 +618,5 @@
     my $preserve = shift;
 
-    die "pathname must be defined" unless ($pathname);
+    ( carp "pathname must be defined" and return undef ) unless ($pathname);
 
     my $fileRef;
@@ -602,7 +627,7 @@
 
     if ($preserve) {
-        # we want to keep the file just create it in the current directory 
+        # we want to keep the file just create it in the current directory
         $fileName = "./$base";
-        open $fileRef, ">$fileName" or die "can't open $fileName for output";
+        open $fileRef, ">$fileName" or ( carp "can't open $fileName for output" and return undef );
     } else {
         #  we really want a tempfile, so put it in /tmp
@@ -621,9 +646,10 @@
     my $scheme = file_scheme($name); # The scheme, e.g., file://, path://
     if (defined $scheme and lc($scheme) eq 'neb') {
-        $self->_neb_start();
-        $status = $self->{nebulous}->delete( $name );
+        $self->_neb_start() or ( carp "Unable to start Nebulous" and return undef );
+        $status = eval { $self->{nebulous}->delete( $name ); };
+        ( carp "Unable to delete Nebulous handle $name" and return undef ) if $@;
     } else {
-        my $resolved = $self->file_resolve($name);
-        if ($resolved && -e $resolved) {
+        my $resolved = $self->file_resolve($name) or return undef;
+        if (defined $resolved and -e $resolved) {
             $status = unlink($resolved);
         }
@@ -638,9 +664,9 @@
     my $name = shift;
 
-    die "need name" unless $name;
+    ( carp "need redirection target" and return undef ) unless $name;
 
     my $filename = $self->file_resolve($name, 1);
 
-    die "cannot resolve $name" unless $filename;
+    ( carp "cannot resolve $name" and return undef ) unless $filename;
 
     if (! open(STDOUT, ">>$filename") ) {
@@ -651,5 +677,6 @@
         while (! open STDOUT, ">>$filename" ) {
             if ($try == $max_tries) {
-                die "failed to redirect stdout to $filename after trying $max_tries times";
+                carp "failed to redirect stdout to $filename after trying $max_tries times";
+                return undef;
             }
             sleep 5;
@@ -658,5 +685,7 @@
         print STDERR "   redirect stdout to $filename succeded on try $try\n";
     }
-    open STDERR, ">>$filename" or die "failed to redirect stderr to $filename";
+    open STDERR, ">>$filename" or ( carp "failed to redirect stderr to $filename" and return undef );
+
+    return 1;
 }
 
@@ -676,5 +705,5 @@
     my $name = shift;                # File name for which to prepare
     my $workdir = shift;        # Working directory
-    my $template = shift;        # Template filename from which to get working directory if 
+    my $template = shift;        # Template filename from which to get working directory if
 
     if (defined $workdir) {
@@ -692,8 +721,8 @@
     # not guaranteed to have a scheme (path:// or file://) - might be /PATH/foobar
     # a relative path (PATH/foobar) is invalid here
-    my $resolved = $self->convert_filename_absolute( $name );
+    my $resolved = $self->convert_filename_absolute( $name ) or return undef;
     my ( $vol, $dirs, $file ) = File::Spec->splitpath( $resolved );
     unless (-d $dirs) {
-        system("mkdir -p $dirs") == 0 or (carp "Can't create directory $dirs" and exit($PS_EXIT_DATA_ERROR));
+        system("mkdir -p $dirs") == 0 or ( carp "Can't create directory $dirs" and return undef );
     }
 
@@ -716,8 +745,8 @@
     # not guaranteed to have a scheme (path:// or file://) - might be /PATH/foobar
     # a relative path (PATH/foobar) is invalid here
-    my $resolved = $self->convert_filename_absolute( $outroot );
+    my $resolved = $self->convert_filename_absolute( $outroot ) or return undef;
     my ( $vol, $dirs, $file ) = File::Spec->splitpath( $resolved );
     unless (-d $dirs) {
-        system("mkdir -p $dirs") == 0 or (carp "Can't create directory $dirs" and exit($PS_EXIT_DATA_ERROR));
+        system("mkdir -p $dirs") == 0 or ( carp "Can't create directory $dirs" and return undef );
     }
 
@@ -734,5 +763,5 @@
     unless (defined $self and defined $name) {
         carp "Programming error";
-        exit($PS_EXIT_PROG_ERROR);
+        return undef;
     }
 
@@ -741,9 +770,9 @@
 
     ## if this is already an absolute path (/PATH/file), just return the path
-    unless (defined $scheme) { 
+    unless (defined $scheme) {
         if ($name =~ m|^/|) { return $name; }
         # without a leading slash, this is an error
         carp "Relative file name provided: relative paths are not permitted.";
-        exit($PS_EXIT_SYS_ERROR);
+        return undef;
     }
 
@@ -751,5 +780,5 @@
 
     if (lc($scheme) eq 'file') {
-        # the above strips of the leading slash; replace it for file:// 
+        # the above strips of the leading slash; replace it for file://
         $name = '/' . $name;
         return $name;
@@ -764,10 +793,6 @@
     }
 
-    # looks like we cannot reach here without an invalid scheme.  
-    # programming error?
-    # return $name;
-
-    carp "Programming error";
-    exit($PS_EXIT_PROG_ERROR);
+    # It's already absolute
+    return $name;
 }
 
@@ -777,10 +802,10 @@
     my $self = shift;                # Configuration object
     my $name = shift;                # raw name
-    
+
     unless (defined $self and defined $name) {
         carp "Programming error";
-        exit($PS_EXIT_PROG_ERROR);
-    }
-    
+        return undef;
+    }
+
     # First, check to see if it's already in a relative form
     my $scheme = file_scheme($name); # The scheme, e.g., file, path
@@ -790,5 +815,5 @@
             # We may as well search for a 'better' path
             # guaranteed to have a scheme (path:// or file://)
-            $name = $self->convert_filename_absolute( $name );
+            $name = $self->convert_filename_absolute( $name ) or return undef;
         } elsif ($scheme eq 'neb') {
             # No chance of changing anything --- move along
@@ -796,8 +821,8 @@
         }
     }
-    
+
     $name = File::Spec->canonpath( $name); # Clean up
     my @dirs = File::Spec->splitdir( $name );
-    
+
     my $path_list = metadataLookupMD($self->{_siteConfig}, 'DATAPATH'); # List of paths
     my $best_path;
@@ -809,5 +834,5 @@
       $path =~ s|/*$||;
       my @path_dirs = File::Spec->splitdir( $path );
-      
+
       # Check if the path is suitable
       next if scalar @path_dirs > scalar @dirs;
@@ -823,5 +848,5 @@
       }
   }
-    
+
     $name =~ s|^/||;
     $name =~ s|/$||;
@@ -845,5 +870,5 @@
     unless (defined $self and defined $name and defined $type) {
         carp "Programming error";
-        exit($PS_EXIT_PROG_ERROR);
+        return undef;
     }
 
@@ -854,13 +879,13 @@
             return undef;
         }
-        
+
         # rejections are saved as a recipe: REJECTIONS
         my @rejContents = `ppConfigDump -dump-recipe REJECTIONS -camera $camera -`;
-        
+
         # load from resulting psMetadataConfig
         $self->{rejection} = $parser->parse( join '', @rejContents); # The rejection metadata
         unless (defined $self->{rejection}) {
             carp "Unable to parse REJECTION recipe for $camera.";
-            exit($PS_EXIT_CONFIG_ERROR);
+            return undef;
         }
     }
@@ -873,5 +898,5 @@
           unless ($item->{class} eq "metadata") {
               carp "$name within REJECTIONS is not of type METADATA";
-              exit($PS_EXIT_PROG_ERROR);
+              return undef;
           }
           my $limits = $item->{value}; # List of rejection limits
@@ -880,7 +905,5 @@
           foreach my $limit (@$limits) {
               if ($limit->{name} eq 'FILTER') {
-                  if ($limit->{value} eq '*' or
-                      (defined $filter and
-                       $limit->{value} eq $filter)) {
+                  if ($limit->{value} eq '*' or (defined $filter and $limit->{value} eq $filter)) {
                       last;
                   }
@@ -888,5 +911,5 @@
               }
           }
-          
+
           foreach my $limit (@$limits) {
               return $limit->{value} if $limit->{name} eq $name;
@@ -915,5 +938,5 @@
     unless (defined $self and defined $name and defined $output) {
         carp "Programming error: required inputs left undefined";
-        exit($PS_EXIT_PROG_ERROR);
+        return undef;
     }
 
@@ -929,5 +952,5 @@
             return undef;
         }
-        
+
         $filerules = metadataLookup($camera, 'FILERULES');        # File rules
         unless (defined $filerules) {
@@ -935,13 +958,15 @@
             return undef;
         }
-        
+
         if ($filerules->{class} eq "scalar" and $filerules->{type} eq "STR") {
             # Allow indirection to a file
             my $filename = $self->_find_config($filerules->{value}); # Resolved filename
+            ( carp "Unable to find file rules file" and return undef ) unless defined $filename;
+
             # Read the file
             my $file;                # File handle
             unless (open $file, $filename) {
                 carp "Unable to open filerules file $filename: $!";
-                exit($PS_EXIT_CONFIG_ERROR);
+                return undef;
             }
             my @contents = <$file>;
@@ -973,5 +998,5 @@
         unless (defined $component) {
             carp "Programming error";
-            exit($PS_EXIT_PROG_ERROR);
+            return undef;
         }
         $filename =~ s/\{CHIP\.NAME\}/$component/;
@@ -980,5 +1005,5 @@
 
     return $filename;
-}   
+}
 
 # Return an EXTNAME From the EXTNAME.RULE table in the camera configuration
@@ -991,5 +1016,5 @@
     unless (defined $self and defined $name) {
         carp "Programming error";
-        exit($PS_EXIT_PROG_ERROR);
+        return undef;
     }
 
@@ -1015,5 +1040,5 @@
         unless (defined $component) {
             carp "Programming error";
-            exit($PS_EXIT_PROG_ERROR);
+            return undef;
         }
         $extname =~ s/\{CHIP\.NAME\}/$component/;
@@ -1021,5 +1046,5 @@
 
     return $extname;
-}   
+}
 
 # Return catdir for tessellation, from TESSELLATIONS within the site configuration
@@ -1031,5 +1056,5 @@
     unless (defined $self and defined $self->{_siteConfig} and defined $tess_id) {
         carp "Programming error";
-        exit($PS_EXIT_PROG_ERROR);
+        return undef;
     }
 
@@ -1037,5 +1062,5 @@
     unless (defined $tessellations) {
         carp "Can't find TESSELLATIONS in site configuration.\n";
-        exit($PS_EXIT_CONFIG_ERROR);
+        return undef;
     }
 
@@ -1050,5 +1075,5 @@
     if (defined $scheme and lc($scheme) eq 'neb') {
         carp "Tessellation $tess_id refers to a Nebulous path: $catdir\n";
-        exit($PS_EXIT_CONFIG_ERROR);
+        return undef;
     }
 
@@ -1065,5 +1090,5 @@
     unless (defined $self and defined $self->{_siteConfig} and defined $dvodb) {
         carp "Programming error";
-        exit($PS_EXIT_PROG_ERROR);
+        return undef;
     }
 
@@ -1071,5 +1096,5 @@
     unless (defined $catdirs) {
         carp "Can't find DVO.CATDIRS in site configuration.\n";
-        exit($PS_EXIT_CONFIG_ERROR);
+        return undef;
     }
 
@@ -1084,5 +1109,5 @@
     if (defined $scheme and lc($scheme) eq 'neb') {
         carp "DVO catdir $dvodb refers to a Nebulous path: $catdir\n";
-        exit($PS_EXIT_CONFIG_ERROR);
+        return undef;
     }
 
@@ -1098,5 +1123,5 @@
     unless (defined $self and defined $self->{_siteConfig} and defined $dvodb) {
         carp "Programming error";
-        exit($PS_EXIT_PROG_ERROR);
+        return undef;
     }
 
@@ -1104,5 +1129,5 @@
     unless (defined $catdirs) {
         carp "Can't find PSASTRO.CATDIRS in site configuration.\n";
-        exit($PS_EXIT_CONFIG_ERROR);
+        return undef;
     }
 
@@ -1117,5 +1142,5 @@
     if (defined $scheme and lc($scheme) eq 'neb') {
         carp "PSASTRO catdir $dvodb refers to a Nebulous path: $catdir\n";
-        exit($PS_EXIT_CONFIG_ERROR);
+        return undef;
     }
 
@@ -1130,5 +1155,5 @@
     unless (defined $self) {
         carp "Programming error";
-        exit($PS_EXIT_PROG_ERROR);
+        return undef;
     }
 
@@ -1158,5 +1183,5 @@
     unless (defined $self and defined $reduction and defined $name) {
         carp "Programming error --- inputs undefined";
-        exit($PS_EXIT_PROG_ERROR);
+        return undef;
     }
 
@@ -1180,13 +1205,14 @@
             return undef;
         }
-        
+
         if ($reductionClasses->{class} eq "scalar" and $reductionClasses->{type} eq "STR") {
             # Allow indirection to a file
             my $filename = $self->_find_config($reductionClasses->{value}); # Resolved filename
+            ( carp "Unable to find reduction classes file" and return undef ) unless defined $filename;
             # Read the file
             my $file;                # File handle
             unless (open $file, $filename) {
                 carp "Unable to open reductionClasses file $filename: $!";
-                exit($PS_EXIT_CONFIG_ERROR);
+                return undef;
             }
             my @contents = <$file>;
@@ -1203,10 +1229,8 @@
 
     my $class = metadataLookupMD($reductionClasses, $reduction) or # Class of interest
-        (carp "Can't find $reduction in REDUCTION in camera configuration.\n" and
-         exit($PS_EXIT_CONFIG_ERROR));
+        ( carp "Can't find $reduction in REDUCTION in camera configuration.\n" and return undef );
 
     my $actual = metadataLookupStr($class, $name) or # The actual recipe name of interest
-        (carp "Can't find $name in $class in REDUCTION in camera configuration.\n" and
-         exit($PS_EXIT_CONFIG_ERROR));
+        (carp "Can't find $name in $class in REDUCTION in camera configuration.\n" and return undef );
 
     return $actual;
@@ -1226,19 +1250,19 @@
     }
 
-    my $dvoImageExtract = can_run('dvoImageExtract') or die "Can't find dvoImageExtract";
-    
+    my $dvoImageExtract = can_run('dvoImageExtract') or ( carp "Can't find dvoImageExtract" and return undef );
+
     my $tess_dir = $self->tessellation_catdir( $tess_id ); # Tessellation catdir for DVO
     unless (defined $tess_dir) {
         carp "Can't get list of tessellations.";
-        return 0;
-    }
-    $tess_dir = $self->convert_filename_absolute( $tess_dir );
+        return undef;
+    }
+    $tess_dir = $self->convert_filename_absolute( $tess_dir ) or return undef;
 
     unless ($self->file_exists( $outname )) {
-        my $outnameResolved = $self->file_create( $outname ); # Resolved filename, for Nebulous
+        my $outnameResolved = $self->file_create( $outname ) or return undef; # Resolved filename, for Nebulous
         my $command = "$dvoImageExtract -D CATDIR $tess_dir $skycell_id -o $outnameResolved";
         my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
             run(command => $command, verbose => $verbose);
-        die "Unable to perform dvoImageExtract for $tess_id $skycell_id\n" unless ($success and $self->file_exists( $outname ));
+        ( carp "Unable to perform dvoImageExtract for $tess_id $skycell_id\n" and return undef ) unless ($success and $self->file_exists( $outname ));
     }
 
@@ -1258,5 +1282,5 @@
         unless (defined $value) {
             carp "Unable to find environment variable $name";
-            exit($PS_EXIT_SYS_ERROR);
+            return undef;
         }
         $dir =~ s/\$\{?$name\}?/$value\//;
@@ -1272,5 +1296,5 @@
     unless (defined $mdc and defined $name) {
         carp "Programming error";
-        exit($PS_EXIT_PROG_ERROR);
+        return undef;
     }
 
@@ -1285,5 +1309,5 @@
     return undef;
 }
-   
+
 
 # Lookup the metadata, checking the type is STR
@@ -1325,5 +1349,5 @@
     unless (defined $mdc and defined $name) {
         carp "Programming error";
-        exit($PS_EXIT_PROG_ERROR);
+        return undef;
     }
 
Index: /branches/cnb_branches/cnb_branch_20090301/PS-IPP-PStamp/Build.PL
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/PS-IPP-PStamp/Build.PL	(revision 23352)
+++ /branches/cnb_branches/cnb_branch_20090301/PS-IPP-PStamp/Build.PL	(revision 23352)
@@ -0,0 +1,15 @@
+use Module::Build;
+# See perldoc Module::Build for details of how this works
+
+Module::Build->new(
+    module_name         => 'PS::IPP::PStamp',
+    dist_version_from   => 'lib/PS/IPP/PStamp.pm',
+    author              => 'Bill Sweeney',
+    license             => 'gpl',
+    create_makefile_pl  => 'passthrough',
+    requires            => {
+        'IPC::Cmd'              => '0.36',
+        'PS::IPP::Config'       => '1.01',
+        'PS::IPP::Metadata::List' => '0.01',
+    },
+)->create_build_script;
Index: /branches/cnb_branches/cnb_branch_20090301/PS-IPP-PStamp/LICENSE
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/PS-IPP-PStamp/LICENSE	(revision 23352)
+++ /branches/cnb_branches/cnb_branch_20090301/PS-IPP-PStamp/LICENSE	(revision 23352)
@@ -0,0 +1,260 @@
+The General Public License (GPL)
+Version 2, June 1991
+
+Copyright (C) 1989, 1991 Free Software Foundation, Inc. 675 Mass Ave,
+Cambridge, MA 02139, USA. Everyone is permitted to copy and distribute
+verbatim copies of this license document, but changing it is not allowed.
+
+Preamble
+
+The licenses for most software are designed to take away your freedom to share
+and change it. By contrast, the GNU General Public License is intended to
+guarantee your freedom to share and change free software--to make sure the
+software is free for all its users. This General Public License applies to most of
+the Free Software Foundation's software and to any other program whose
+authors commit to using it. (Some other Free Software Foundation software is
+covered by the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+When we speak of free software, we are referring to freedom, not price. Our
+General Public Licenses are designed to make sure that you have the freedom
+to distribute copies of free software (and charge for this service if you wish), that
+you receive source code or can get it if you want it, that you can change the
+software or use pieces of it in new free programs; and that you know you can do
+these things.
+
+To protect your rights, we need to make restrictions that forbid anyone to deny
+you these rights or to ask you to surrender the rights. These restrictions
+translate to certain responsibilities for you if you distribute copies of the
+software, or if you modify it.
+
+For example, if you distribute copies of such a program, whether gratis or for a
+fee, you must give the recipients all the rights that you have. You must make
+sure that they, too, receive or can get the source code. And you must show
+them these terms so they know their rights.
+
+We protect your rights with two steps: (1) copyright the software, and (2) offer
+you this license which gives you legal permission to copy, distribute and/or
+modify the software.
+
+Also, for each author's protection and ours, we want to make certain that
+everyone understands that there is no warranty for this free software. If the
+software is modified by someone else and passed on, we want its recipients to
+know that what they have is not the original, so that any problems introduced by
+others will not reflect on the original authors' reputations.
+
+Finally, any free program is threatened constantly by software patents. We wish
+to avoid the danger that redistributors of a free program will individually obtain
+patent licenses, in effect making the program proprietary. To prevent this, we
+have made it clear that any patent must be licensed for everyone's free use or
+not licensed at all.
+
+The precise terms and conditions for copying, distribution and modification
+follow.
+
+GNU GENERAL PUBLIC LICENSE
+TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND
+MODIFICATION
+
+0. This License applies to any program or other work which contains a notice
+placed by the copyright holder saying it may be distributed under the terms of
+this General Public License. The "Program", below, refers to any such program
+or work, and a "work based on the Program" means either the Program or any
+derivative work under copyright law: that is to say, a work containing the
+Program or a portion of it, either verbatim or with modifications and/or translated
+into another language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not covered by
+this License; they are outside its scope. The act of running the Program is not
+restricted, and the output from the Program is covered only if its contents
+constitute a work based on the Program (independent of having been made by
+running the Program). Whether that is true depends on what the Program does.
+
+1. You may copy and distribute verbatim copies of the Program's source code as
+you receive it, in any medium, provided that you conspicuously and appropriately
+publish on each copy an appropriate copyright notice and disclaimer of warranty;
+keep intact all the notices that refer to this License and to the absence of any
+warranty; and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and you may at
+your option offer warranty protection in exchange for a fee.
+
+2. You may modify your copy or copies of the Program or any portion of it, thus
+forming a work based on the Program, and copy and distribute such
+modifications or work under the terms of Section 1 above, provided that you also
+meet all of these conditions:
+
+a) You must cause the modified files to carry prominent notices stating that you
+changed the files and the date of any change.
+
+b) You must cause any work that you distribute or publish, that in whole or in
+part contains or is derived from the Program or any part thereof, to be licensed
+as a whole at no charge to all third parties under the terms of this License.
+
+c) If the modified program normally reads commands interactively when run, you
+must cause it, when started running for such interactive use in the most ordinary
+way, to print or display an announcement including an appropriate copyright
+notice and a notice that there is no warranty (or else, saying that you provide a
+warranty) and that users may redistribute the program under these conditions,
+and telling the user how to view a copy of this License. (Exception: if the
+Program itself is interactive but does not normally print such an announcement,
+your work based on the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If identifiable
+sections of that work are not derived from the Program, and can be reasonably
+considered independent and separate works in themselves, then this License,
+and its terms, do not apply to those sections when you distribute them as
+separate works. But when you distribute the same sections as part of a whole
+which is a work based on the Program, the distribution of the whole must be on
+the terms of this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest your rights to
+work written entirely by you; rather, the intent is to exercise the right to control
+the distribution of derivative or collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program with the
+Program (or with a work based on the Program) on a volume of a storage or
+distribution medium does not bring the other work under the scope of this
+License.
+
+3. You may copy and distribute the Program (or a work based on it, under
+Section 2) in object code or executable form under the terms of Sections 1 and 2
+above provided that you also do one of the following:
+
+a) Accompany it with the complete corresponding machine-readable source
+code, which must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange; or,
+
+b) Accompany it with a written offer, valid for at least three years, to give any
+third party, for a charge no more than your cost of physically performing source
+distribution, a complete machine-readable copy of the corresponding source
+code, to be distributed under the terms of Sections 1 and 2 above on a medium
+customarily used for software interchange; or,
+
+c) Accompany it with the information you received as to the offer to distribute
+corresponding source code. (This alternative is allowed only for noncommercial
+distribution and only if you received the program in object code or executable
+form with such an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for making
+modifications to it. For an executable work, complete source code means all the
+source code for all modules it contains, plus any associated interface definition
+files, plus the scripts used to control compilation and installation of the
+executable. However, as a special exception, the source code distributed need
+not include anything that is normally distributed (in either source or binary form)
+with the major components (compiler, kernel, and so on) of the operating system
+on which the executable runs, unless that component itself accompanies the
+executable.
+
+If distribution of executable or object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the source
+code from the same place counts as distribution of the source code, even though
+third parties are not compelled to copy the source along with the object code.
+
+4. You may not copy, modify, sublicense, or distribute the Program except as
+expressly provided under this License. Any attempt otherwise to copy, modify,
+sublicense or distribute the Program is void, and will automatically terminate
+your rights under this License. However, parties who have received copies, or
+rights, from you under this License will not have their licenses terminated so long
+as such parties remain in full compliance.
+
+5. You are not required to accept this License, since you have not signed it.
+However, nothing else grants you permission to modify or distribute the Program
+or its derivative works. These actions are prohibited by law if you do not accept
+this License. Therefore, by modifying or distributing the Program (or any work
+based on the Program), you indicate your acceptance of this License to do so,
+and all its terms and conditions for copying, distributing or modifying the
+Program or works based on it.
+
+6. Each time you redistribute the Program (or any work based on the Program),
+the recipient automatically receives a license from the original licensor to copy,
+distribute or modify the Program subject to these terms and conditions. You
+may not impose any further restrictions on the recipients' exercise of the rights
+granted herein. You are not responsible for enforcing compliance by third parties
+to this License.
+
+7. If, as a consequence of a court judgment or allegation of patent infringement
+or for any other reason (not limited to patent issues), conditions are imposed on
+you (whether by court order, agreement or otherwise) that contradict the
+conditions of this License, they do not excuse you from the conditions of this
+License. If you cannot distribute so as to satisfy simultaneously your obligations
+under this License and any other pertinent obligations, then as a consequence
+you may not distribute the Program at all. For example, if a patent license would
+not permit royalty-free redistribution of the Program by all those who receive
+copies directly or indirectly through you, then the only way you could satisfy
+both it and this License would be to refrain entirely from distribution of the
+Program.
+
+If any portion of this section is held invalid or unenforceable under any particular
+circumstance, the balance of the section is intended to apply and the section as
+a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any patents or other
+property right claims or to contest validity of any such claims; this section has
+the sole purpose of protecting the integrity of the free software distribution
+system, which is implemented by public license practices. Many people have
+made generous contributions to the wide range of software distributed through
+that system in reliance on consistent application of that system; it is up to the
+author/donor to decide if he or she is willing to distribute software through any
+other system and a licensee cannot impose that choice.
+
+This section is intended to make thoroughly clear what is believed to be a
+consequence of the rest of this License.
+
+8. If the distribution and/or use of the Program is restricted in certain countries
+either by patents or by copyrighted interfaces, the original copyright holder who
+places the Program under this License may add an explicit geographical
+distribution limitation excluding those countries, so that distribution is permitted
+only in or among countries not thus excluded. In such case, this License
+incorporates the limitation as if written in the body of this License.
+
+9. The Free Software Foundation may publish revised and/or new versions of the
+General Public License from time to time. Such new versions will be similar in
+spirit to the present version, but may differ in detail to address new problems or
+concerns.
+
+Each version is given a distinguishing version number. If the Program specifies a
+version number of this License which applies to it and "any later version", you
+have the option of following the terms and conditions either of that version or of
+any later version published by the Free Software Foundation. If the Program does
+not specify a version number of this License, you may choose any version ever
+published by the Free Software Foundation.
+
+10. If you wish to incorporate parts of the Program into other free programs
+whose distribution conditions are different, write to the author to ask for
+permission. For software which is copyrighted by the Free Software Foundation,
+write to the Free Software Foundation; we sometimes make exceptions for this.
+Our decision will be guided by the two goals of preserving the free status of all
+derivatives of our free software and of promoting the sharing and reuse of
+software generally.
+
+NO WARRANTY
+
+11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS
+NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE
+COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM
+"AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR
+IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
+ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE,
+YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR
+CORRECTION.
+
+12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED
+TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY
+WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS
+PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES
+ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM
+(INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY
+OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS
+BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+END OF TERMS AND CONDITIONS
Index: /branches/cnb_branches/cnb_branch_20090301/PS-IPP-PStamp/MANIFEST
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/PS-IPP-PStamp/MANIFEST	(revision 23352)
+++ /branches/cnb_branches/cnb_branch_20090301/PS-IPP-PStamp/MANIFEST	(revision 23352)
@@ -0,0 +1,6 @@
+Build.PL
+LICENSE
+MANIFEST
+lib/PS/IPP/PStamp.pm
+lib/PS/IPP/PStamp/Job.pm
+lib/PS/IPP/PStamp/RequestFile.pm
Index: /branches/cnb_branches/cnb_branch_20090301/PS-IPP-PStamp/lib/PS/IPP/PStamp.pm
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/PS-IPP-PStamp/lib/PS/IPP/PStamp.pm	(revision 23352)
+++ /branches/cnb_branches/cnb_branch_20090301/PS-IPP-PStamp/lib/PS/IPP/PStamp.pm	(revision 23352)
@@ -0,0 +1,87 @@
+
+package PS::IPP::PStamp;
+
+use strict;
+use warnings;
+
+use vars qw($VERSION);
+$VERSION = '1.0';
+
+=pod
+
+=head1 NAME
+
+PStamp - Perl Module of Postage Stamp Server functions
+
+=head1 SYNOPSIS
+
+    use PStamp;
+
+    # equivalent to:
+
+    use Pstamp::RequestFile;
+
+=head1 DESCRIPTION
+
+This is a convenience module so that that don't have to individualy load (C<use
+...;>) all of the common PStamp inteface modules.  Please see the POD of the
+individual modules for usage information. 
+
+=head1 USAGE
+
+=head2 Import Parameters
+
+This module accepts no arguments to it's C<import> method and exports no
+I<symbols>.
+
+=head2 Methods
+
+None.
+
+=head1 EXAMPLE PROGRAM
+
+=cut
+
+use PStamp::RequestFile qw( :standard );
+use PStamp::Job qw( :standard );
+
+=head1 CREDITS
+
+
+=head1 SUPPORT
+
+Please contact the author directly via e-mail.
+
+=head1 AUTHOR
+
+Bill Sweeney
+
+=head1 COPYRIGHT
+
+Copyright (C) 2008  Bill Sweeney.  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 LICENSE file included with
+this module, or in the L<perlgpl> Pod as supplied with Perl 5.8.1 and later.
+
+=head1 SEE ALSO
+
+L<PStamp::RequestFile>
+
+=cut
+
+1;
+
+__END__
Index: /branches/cnb_branches/cnb_branch_20090301/PS-IPP-PStamp/lib/PS/IPP/PStamp/Job.pm
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/PS-IPP-PStamp/lib/PS/IPP/PStamp/Job.pm	(revision 23352)
+++ /branches/cnb_branches/cnb_branch_20090301/PS-IPP-PStamp/lib/PS/IPP/PStamp/Job.pm	(revision 23352)
@@ -0,0 +1,576 @@
+###
+###     PStamp/Job.pm
+###     subroutines and constants related to Postage Stamp Jobs
+###
+
+package PS::IPP::PStamp::Job;
+
+use strict;
+use warnings;
+
+our $VERSION = '1.0';
+
+use base qw( Exporter );
+
+our @EXPORT_OK = qw( 
+                    locate_images
+                    resolve_project
+                    );
+our %EXPORT_TAGS = (standard => [@EXPORT_OK]);
+
+
+use IPC::Cmd 0.36 qw( can_run run );
+
+use PS::IPP::Metadata::List qw( parse_md_list );
+use PS::IPP::Config qw( :standard );
+
+### my @images = locate_images($image_db, $req_type, $img_type, $id, $component,
+###            $mjd_min, $mjd_max, $filter);
+
+sub locate_images {
+    my $ipprc    = shift;   # required
+    my $image_db = shift;   # required
+    my $req_type = shift;   # required
+    my $img_type = shift;   # required
+    my $id       = shift;   # required unless req_type eq bycoord
+    my $component= shift;   # class_id or skycell_id
+    my $x        = shift;
+    my $y        = shift;
+    my $mjd_min  = shift;
+    my $mjd_max  = shift;
+    my $filter   = shift;
+    my $verbose  = shift;
+
+    # we die in response to bad data in request files
+    die "Unknown req_type: $req_type" if ($req_type ne "byid") and ($req_type ne "byexp")
+                                        and ($req_type ne "bycoord") and ($req_type ne "bydiff");
+    if (($req_type eq "byid") and ($img_type eq "diff")) {
+        # lookups of all of the information for diff images requires a two level lookup to
+        # get the exposure information. Switching the req_type allows us to keep that code
+        # in one place
+        $req_type = "bydiff";
+    }
+    if ($req_type eq "bydiff") {
+        my $results = lookup_bydiff($ipprc, $image_db, $id, $img_type, $verbose);
+        if (!$results) {
+            return undef;
+        }
+        if (($img_type eq "warp") or ($img_type eq "diff")) {
+            # lookup_bydiff has done all of the work
+            return [$results];
+        } elsif (($img_type eq "raw") or ($img_type eq "chip")) {
+            $req_type = "byexp";
+            $id = $results->{exp_name};
+            return undef if !$id;
+            # fall through and lookup byexp
+#        } elsif ($img_type eq "warp") {
+#            $req_type = "byid";
+#            $id = $results->{warp_id};
+#            $component = $results->{skycell_id};
+#            return undef if !$id;
+#            # fall through and lookup by warp_id 
+        } elsif ($img_type eq "stack") {
+            $req_type = "byid";
+            $id = $results->{stack_id};
+            return undef if !$id;
+            # fall though and lookup by stack id
+        } else {
+            # shouldn't I check this elsewhere?
+            print STDERR "Error: $img_type is an unknown image type\n";
+            return undef;
+        }
+
+    } elsif ($req_type eq "bycoord") {
+
+        # run
+        # regtool -dbname $image_db -processedimfile -time_begin $mjd_min -time_end = $mjd_max -filter $filter
+        #
+        my $results = lookup_bycoord($ipprc, $image_db, $x, $y, $mjd_min, $mjd_max, $filter, $verbose);
+
+        # now take the results and lookup byexp
+        # XXX: This needs work. What stops results from returning multiple images????
+        # We should loop over the set of images returned building up the full results array
+        $req_type = "byexp";
+        $id = $results->{exp_name};
+    }
+
+    my $results = lookup($ipprc, $image_db, $req_type, $img_type, $id, $component, $verbose);
+
+    return $results;
+}
+
+sub lookup {
+    my $ipprc    = shift;
+    my $image_db = shift;
+    my $req_type = shift;
+    my $img_type = shift;
+    my $id       = shift;
+    my $component= shift;
+    my $verbose  = shift;
+
+    my $missing_tools;
+    my $regtool = can_run('regtool') or (warn "Can't find regtool" and $missing_tools = 1);
+    my $chiptool = can_run('chiptool') or (warn "Can't find chiptool" and $missing_tools = 1);
+    my $warptool = can_run('warptool') or (warn "Can't find warptool" and $missing_tools = 1);
+    my $difftool = can_run('difftool') or (warn "Can't find difftool" and $missing_tools = 1);
+    my $stacktool = can_run('stacktool') or (warn "Can't find stacktool" and $missing_tools = 1);
+    if ($missing_tools) {
+        warn("Can't find required tools.");
+        exit ($PS_EXIT_CONFIG_ERROR);
+    }
+    my $command;
+    my $id_opt;     # option for the lookup
+    my $mask_name;
+    my $weight_name;
+    my $base_name;
+    my $want_astrom;
+    my $set_class_id;
+    my $class_id;
+    my $skycell_id;
+
+    # special class_id value "null" means ignore
+    if ($component eq "null") {
+        $component = undef;
+    }
+
+    if ($img_type eq "raw") {
+        $class_id = $component;
+        $command = "$regtool -processedimfile -dbname $image_db";
+        $id_opt = "-exp_id";
+        $command .= " -class_id $class_id" if $component;
+        $want_astrom = 1;
+        $set_class_id = 1;
+    } elsif ($img_type eq "chip") {
+        $class_id = $component;
+        $command = "$chiptool -processedimfile -dbname $image_db";
+        $command .= " -class_id $class_id" if $class_id;
+        $id_opt = "-chip_id";
+        $mask_name    = "PPIMAGE.CHIP.MASK";
+        $weight_name  = "PPIMAGE.CHIP.VARIANCE";
+        $base_name    = "path_base"; # name of the field for the chiptool output
+        $want_astrom  = 1;
+        $set_class_id = 1;
+    } elsif ($img_type eq "warp") {
+        $skycell_id = $component;
+        $command = "$warptool -warped -dbname $image_db";
+        $command .= " -skycell_id $skycell_id" if $skycell_id;
+        $id_opt = "-warp_id";
+        $mask_name    = "PSWARP.OUTPUT.MASK";
+        $weight_name  = "PSWARP.OUTPUT.VARIANCE";
+        $base_name    = "path_base"; # name of the field for the warptool output
+    } elsif ($img_type eq "diff") {
+        $skycell_id = $component;
+        $command = "$difftool -diffskyfile -dbname $image_db";
+        $command .= " -skycell_id $skycell_id" if $skycell_id;
+        $id_opt = "-diff_id";
+        $mask_name   = "PPSUB.OUTPUT.MASK";
+        $weight_name = "PPSUB.OUTPUT.VARIANCE";
+        $base_name   = "path_base";
+    } elsif ($img_type eq "stack") {
+        $command = "$stacktool -sumskyfile -dbname $image_db";
+        $id_opt = "-stack_id";
+
+        $mask_name   = "PPSTACK.OUTPUT.MASK";
+        $weight_name = "PPSTACK.OUTPUT.VARIANCE";
+        $base_name   = "path_base";
+    } else {
+        die "Unknown img_type supplied: $img_type";
+    }
+
+    if ($req_type eq "byid") {
+        $command .= " $id_opt $id";
+    } elsif ($req_type eq "byexp") {
+        $command .= " -exp_name $id";
+    } else {
+        die "Unknown req_type supplied: $req_type";
+    }
+
+    # run the tool and parse the output
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+                run(command => $command, verbose => $verbose);
+    unless ($success) {
+        # not sure if we should die here
+        print STDERR @$stderr_buf;
+        return undef;
+    }
+
+    my $buf = join "", @$stdout_buf;
+    if (!$buf) {
+        return;
+    }
+
+    my $mdcParser = PS::IPP::Metadata::Config->new;
+    my $images = parse_md_fast($mdcParser, $buf)
+        or die ("Unable to parse metadata config doc");
+
+    my $output = [];
+
+    my $camera;
+    foreach my $image (@$images) {
+        my $base;
+
+        next if ($img_type eq "warp" and $image->{ignored});
+
+        if ($base_name) {
+            $base = $image->{$base_name};
+            # if base is undef then the output pruducts for this image are incomplete
+            # This may only happen for warps. (Actually the test for ignored that I added
+            # just above probably solved the problem I was trying to solve with this test)
+            next if !$base;
+        }
+        if (!$camera) {
+            # This assumes that all images have the same camera
+            $camera = $image->{camera};
+            $ipprc->define_camera($camera);
+        }
+        my $out = {};
+        $out->{exp_id} = $image->{exp_id};
+        $out->{exp_name} = $image->{exp_name};
+        $out->{image}  = $image->{uri};
+        $out->{state}  = $image->{state}; # state is undef for rawExp, but that's ok
+        if ($set_class_id) {
+            $class_id = $image->{class_id};
+            $out->{class_id} = $class_id;
+        }
+
+        # find the mask and weight images
+        if ($base) {
+            $out->{mask}   = $ipprc->filename($mask_name,   $base, $class_id) if $mask_name;
+            $out->{weight} = $ipprc->filename($weight_name, $base, $class_id) if $weight_name;
+        }
+        $out->{camera} = $camera;
+
+        $out->{astrom} = find_astrometry($ipprc, $image_db, $image, $verbose) if $want_astrom;
+
+        push @$output, $out;
+    }
+
+    return $output;
+}
+sub lookup_bydiff {
+    my $ipprc    = shift;
+    my $image_db = shift;
+    my $id       = shift;
+    my $img_type = shift;
+    my $verbose = shift;
+
+    my $missing_tools;
+    my $difftool = can_run('difftool') or (warn "Can't find difftool" and $missing_tools = 1);
+    if ($missing_tools) {
+        warn("Can't find required tools.");
+        exit ($PS_EXIT_CONFIG_ERROR);
+    }
+
+    my $command = "$difftool -diffskyfile -diff_image_id $id -dbname $image_db";
+
+    # run the tool and parse the output
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+                run(command => $command, verbose => $verbose);
+    unless ($success) {
+        # not sure if we should die here
+        print STDERR @$stderr_buf;
+        return undef;
+    }
+
+    my $buf = join "", @$stdout_buf;
+    if (!$buf) {
+        return undef;
+    }
+
+    my $mdcParser = PS::IPP::Metadata::Config->new;
+    my $images = parse_md_fast($mdcParser, $buf)
+        or die ("Unable to parse metadata config doc");
+
+    my $n = @$images;
+    if ($n > 1) {
+        die ("difftool returned an unexpected number of diffskyfiles: $n");
+    } elsif ($n == 0) {
+        return undef;
+    }
+
+    my $image = $images->[0];
+
+    my $skycell_id = $image->{skycell_id};
+
+    if ($image->{fault}) {
+        print STDERR "selected difference image $id $image->{diff_id} $skycell_id has fault: $image->{fault}\n";
+        return undef;
+    }
+
+    # The standard way to do a diff is warp - stack 
+    # so we interpret the requested image in that way
+
+    my $stack_id = $image->{stack2};
+    # XXX difftool currently returns max long long for null
+    # this line is ready if we switch the code to return zero for null
+    if ($stack_id and ($stack_id != 9223372036854775807)) {
+        $image->{stack_id} = $stack_id;
+    }
+
+    my $warp_id =  $image->{warp1};
+    # XXX difftool currently returns max long long for null
+    # this line is ready if we switch the code to return zero for null
+    if ($warp_id and ($warp_id != 9223372036854775807)) {
+        $image->{warp_id} = $warp_id;
+
+        ## now use the warp and go get the exposure information
+        $command = "warptool -warped -warp_id $warp_id -skycell_id $skycell_id -dbname $image_db -limit 1";
+        my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+                    run(command => $command, verbose => $verbose);
+        unless ($success) {
+            # not sure if we should die here
+            print STDERR @$stderr_buf;
+            return undef;
+        }
+
+        my $buf = join "", @$stdout_buf;
+        if (!$buf) {
+            return undef;
+        }
+
+        my $mdcParser = PS::IPP::Metadata::Config->new;
+        my $warps = parse_md_fast($mdcParser, $buf)
+            or die ("Unable to parse metadata config doc");
+        my $warp = $warps->[0];
+
+        if ($img_type eq "warp") {
+            $image = $warp;
+        } else {
+            $image->{exp_id} = $warp->{exp_id};
+            $image->{exp_name} = $warp->{exp_name};
+            $image->{camera} = $warp->{camera};
+        }
+    }
+
+    if (($img_type eq "diff") or ($img_type eq "warp")) {
+        # the $image is going to be returned directly in this case so we need to duplicate
+        # some of processing that lookup does for other img_types
+        $image->{image} = $image->{uri};
+        if ($image->{camera}) {
+            $ipprc->define_camera($image->{camera});
+            if ($img_type eq "diff") {
+                $image->{mask}   = $ipprc->filename("PPSUB.OUTPUT.MASK", $image->{path_base});
+                $image->{weight} = $ipprc->filename("PPSUB.OUTPUT.VARIANCE", $image->{path_base});
+            } else {
+                $image->{mask}   = $ipprc->filename("PSWARP.OUTPUT.MASK",   $image->{path_base});
+                $image->{weight} = $ipprc->filename("PSWARP.OUTPUT.VARIANCE", $image->{path_base});
+            }
+        } else {
+            # XXX this will only happen if the minuend is not a warp
+            print STDERR "WARNING: cannot resolve camera so cannot get weight and mask images\n";
+        }
+    }
+
+    return $image;
+}
+
+sub lookup_bycoord {
+    my $ipprc    = shift;
+    my $image_db = shift;
+    my $x        = shift;
+    my $y        = shift;
+    my $mjd_min  = shift;
+    my $mjd_max  = shift;
+    my $filter   = shift;
+    my $verbose  = shift;
+
+    my $missing_tools;
+    my $regtool = can_run('regtool') or (warn "Can't find regtool" and $missing_tools = 1);
+    if ($missing_tools) {
+        warn("Can't find required tools.");
+        exit ($PS_EXIT_CONFIG_ERROR);
+    }
+
+    # XXX TODO:
+    # Full lookups by coordinate require looking at the DVO database. However, 
+    # dateobs and filter which is all that MOPS has asked for.
+
+    my $args;
+    if ($mjd_min) {
+        my $dateobs_min = mjd_to_dateobs($mjd_min);
+        $args .= " -dateobs_begin $dateobs_min";
+    }
+    if ($mjd_max) {
+        my $dateobs_max = mjd_to_dateobs($mjd_max);
+        $args .= " -dateobs_end $dateobs_max";
+    }
+    if ($filter) {
+        $args .= " -filter $filter";
+    }
+
+    if (!$args) {
+        # avoid returning every exposure in the DB
+        print STDERR "no query arguments provided for bycoord\n";
+        return undef;
+    }
+
+    # XXX TODO: This query doesn't work Something appears to be out of
+    # sync about the times I'm passing and what regtool and or the DB expect
+    # the query I used in the C version of locateimages used
+    # WHERE dateobs >= FROM_UNIXTIME(%ld) AND dateobs <= FROM_UNIXTIME(%ld + exp_time)
+
+    my $command = "$regtool -processedexp -dbname $image_db $args";
+
+    # run the tool and parse the output
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+                run(command => $command, verbose => $verbose);
+    unless ($success) {
+        # not sure if we should die here
+        print STDERR @$stderr_buf;
+        return undef;
+    }
+    my $mdcParser = PS::IPP::Metadata::Config->new; # Parser for metadata config files
+
+    my $output = join "", @$stdout_buf;
+    if (!$output) {
+        print STDERR "no output returned from $command\n" if $verbose;
+        return undef;
+    }
+    my $images = parse_md_fast($mdcParser, $output) or die ("Unable to parse metadata config doc");
+
+    return $images;
+}
+
+# find the astrometry file for a given exposure
+# return undef if no completed camRun exists
+sub find_astrometry {
+    my $ipprc = shift;
+    my $image_db = shift;
+    my $image = shift;      # hashref to output of the lookup tool
+    my $verbose = shift;
+
+    my $missing_tools;
+    my $camtool = can_run("camtool") or (warn "Can't find camtool" and $missing_tools = 1);
+    my $ppConfigDump = can_run('ppConfigDump') or (warn "Can't find ppConfigDump" and $missing_tools = 1);
+    if ($missing_tools) {
+        warn("Can't find required tools.");
+        exit ($PS_EXIT_CONFIG_ERROR);
+    }
+
+    my $command = "$camtool -dbname $image_db -processedexp -exp_id $image->{exp_id}";
+    # run the tool and parse the output
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+                run(command => $command, verbose => $verbose);
+    unless ($success) {
+        print STDERR @$stderr_buf;
+        return undef;
+    }
+
+    my $mdcParser = PS::IPP::Metadata::Config->new; # Parser for metadata config files
+
+    my $output = join "", @$stdout_buf;
+    if (!$output) {
+        print STDERR "no output returned from $command\n" if $verbose;
+        return undef;
+    }
+    my $camruns = parse_md_fast($mdcParser, $output);
+    if (!$camruns) {
+        return undef;
+    }
+
+    # If there are multiple cam runs for this exposure, take the last one
+    # assuming that it has the best astrometry 
+    my $camdata = pop @$camruns;
+    if (!$camdata) {
+        # no cam runs for this exposure id therefore best astrometry is whatever is in the header
+        return undef;
+    }
+    my $camRoot = $camdata->{path_base};
+    my $camera = $image->{camera};
+    my $astromSource;
+    {
+       my $command = "$ppConfigDump -camera $camera -dump-recipe PSWARP -";
+        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);
+            die("Unable to perform ppConfigDump: $error_code");
+        }
+        my $metadata = $mdcParser->parse(join "", @$stdout_buf) or
+            die("Unable to parse metadata config doc");
+        $astromSource = metadataLookupStr($metadata, 'ASTROM.SOURCE');
+    }
+
+    my $astromFile = $ipprc->filename($astromSource, $camRoot);
+
+    return $astromFile;
+}
+
+# splits meta data config input stream into single units to work around the pathalogically
+# slow parser. This is similar to and adapted from code in various ippScripts.
+sub parse_md_fast {
+    my $mdcParser = shift;
+    my $input = shift;
+    my $output = ();
+
+    my @whole = split /\n/, $input;
+    my @single = ();
+
+    my $n;
+    while ( ($n = @whole) > 0) {
+        my $value = shift @whole;
+        push @single, $value;
+        if ($value =~ /^\s*END\s*$/) {
+	    push @single, "\n";
+
+            my $list = parse_md_list( $mdcParser->parse( join("\n", @single ) ) ) or
+                print STDERR "Unable to parse metdata config doc" and return undef;
+#            my $num = @$list;
+#            print STDERR "list has $num elments\n";
+            push @$output, $list->[0];
+
+            @single = ();
+        }
+    }
+    return $output;
+}
+
+sub mjd_to_dateobs {
+    my $mjd = shift;
+
+    my $ticks = ($mjd - 40587.0) * 86400;
+
+    my ($sec, $min, $hr, $day, $mon, $year) = gmtime($ticks);
+
+    return sprintf "'%4d-%02d-%02d %02d:%02d:%02d'", $year+1900, $mon+1, $day, $hr, $min, $sec;
+}
+
+# resolve_project()
+# get project specific information
+sub resolve_project {
+    my $ipprc = shift;
+    my $project_name = shift;
+    my $dbname = shift;
+    die "project is not defined" if !$project_name;
+
+    my $verbose = 0;
+
+    my $missing_tools;
+    my $pstamptool = can_run("pstamptool") or (warn "Can't find pstamptool" and $missing_tools = 1);
+    if ($missing_tools) {
+        warn("Can't find required tools.");
+        exit ($PS_EXIT_CONFIG_ERROR);
+    }
+
+    my $command = "$pstamptool -project -name $project_name";
+    $command .= " -dbname $dbname" if defined $dbname;
+    # run the tool and parse the output
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+                run(command => $command, verbose => $verbose);
+    unless ($success) {
+        print STDERR @$stderr_buf;
+        return undef;
+    }
+
+    my $mdcParser = PS::IPP::Metadata::Config->new; # Parser for metadata config files
+
+    my $output = join "", @$stdout_buf;
+    if (!$output) {
+        print STDERR "no output returned from $command\n" if $verbose;
+        return undef;
+    }
+    my $proj_hash = parse_md_fast($mdcParser, $output);
+
+    return $proj_hash->[0];
+}
+1;
Index: /branches/cnb_branches/cnb_branch_20090301/PS-IPP-PStamp/lib/PS/IPP/PStamp/RequestFile.pm
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/PS-IPP-PStamp/lib/PS/IPP/PStamp/RequestFile.pm	(revision 23352)
+++ /branches/cnb_branches/cnb_branch_20090301/PS-IPP-PStamp/lib/PS/IPP/PStamp/RequestFile.pm	(revision 23352)
@@ -0,0 +1,129 @@
+#!/bin/env perl
+
+###
+###     PStamp/RequestFile.pm
+###     subroutines and constants related to Postage Stamp Request Files
+###
+
+package PS::IPP::PStamp::RequestFile;
+
+use strict;
+use warnings;
+
+our $VERSION = '1.0';
+
+use base qw( Exporter );
+
+our @EXPORT_OK = qw( 
+                    read_request_file
+                    $PSTAMP_CENTER_IN_PIXELS
+                    $PSTAMP_RANGE_IN_PIXELS
+                    $PSTAMP_SELECT_IMAGE
+                    $PSTAMP_SELECT_MASK
+                    $PSTAMP_SELECT_WEIGHT
+                    );
+our %EXPORT_TAGS = (standard => [@EXPORT_OK]);
+
+
+our $PSTAMP_CENTER_IN_PIXELS = 1;
+our $PSTAMP_RANGE_IN_PIXELS  = 2;
+
+our $PSTAMP_SELECT_IMAGE     = 1;
+our $PSTAMP_SELECT_MASK      = 2;
+our $PSTAMP_SELECT_WEIGHT    = 4;
+
+use IPC::Cmd 0.36 qw( can_run run );
+
+use PS::IPP::Metadata::List qw( parse_md_list );
+use PS::IPP::Config qw( :standard );
+
+sub read_request_file {
+    my $request_file_name = shift;
+    die "need request file name\n" unless $request_file_name;
+
+    my $verbose = shift;
+
+    my $ipprc = PS::IPP::Config->new(); # IPP Configuration
+
+    my $missing_tools;
+
+    my $pstampdump  = can_run('pstampdump') or (warn "Can't find pstampdump" and $missing_tools = 1);
+    my $fields  = can_run('fields') or (warn "Can't find fields" and $missing_tools = 1);
+
+    if ($missing_tools) {
+        warn("Can't find required tools.");
+        exit ($PS_EXIT_CONFIG_ERROR);
+    }
+
+    # Parser for metadata config files
+    my $mdcParser = PS::IPP::Metadata::Config->new;
+
+    #
+    # get the data from the extension header
+    #
+    my $fields_output;
+    {
+        my $command = "echo $request_file_name | $fields -x 0 EXTNAME EXTVER REQ_NAME";
+        my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+            run(command => $command, verbose => $verbose);
+        $fields_output = join "", @$stdout_buf;
+    }
+    my (undef, $extname, $extver, $req_name) = split " ", $fields_output;
+
+    # make sure the file contains what we are expecting
+
+    die "$request_file_name is not a PS1_PS_REQUEST" 
+                    if !$extname or ($extname ne "PS1_PS_REQUEST");
+    die "REQ_NAME not found in $request_file_name"  if (!$req_name);
+    die "wrong EXTVER $extver found in $request_file_name" if ($extver ne "1");
+
+    my %header;
+    $header{REQ_NAME} = $req_name;
+    $header{EXTVER}   = $extver;
+    $header{EXTNAME}  = $extname;
+
+    #
+    # now convert the request table to an array of hashes
+    #
+
+    my $rows;
+    {
+        my $command = "$pstampdump $request_file_name";
+        my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+            run(command => $command, verbose => $verbose);
+        unless ($success) {
+            print STDERR @$stderr_buf;
+        }
+        my $table =  $mdcParser->parse(join "", @$stdout_buf) or
+            die("Unable to parse metdata config doc");
+
+        $rows = parse_md_list($table);
+    }
+
+    my %req_specs;
+    foreach my $row (@$rows) {
+        my $rownum   = $row->{ROWNUM};
+        $req_specs{$rownum} = $row;
+
+        my $job_type = $row->{JOB_TYPE};
+        my $project  = $row->{PROJECT};
+        my $req_type = $row->{REQ_TYPE};
+        my $img_type = $row->{IMG_TYPE};
+        my $id       = $row->{ID};
+        my $class_id = $row->{CLASS_ID};
+        my $stamp_name   = $row->{STAMP_NAME};
+        my $filter   = $row->{REQFILT};
+        my $mjd_min = $row->{MJD_MIN};
+        my $mjd_max = $row->{MJD_MAX};
+        my $x = $row->{CENTER_X};
+        my $y = $row->{CENTER_Y};
+        my $w = $row->{WIDTH};
+        my $h = $row->{HEIGHT};
+        my $coord_mask = $row->{COORD_MASK};
+        my $option_mask= $row->{OPTION_MASK};
+
+        print "$rownum $req_type $img_type $id\n" if $verbose;
+    }
+    return (\%header, \%req_specs);
+}
+1;
Index: /branches/cnb_branches/cnb_branch_20090301/archive/conv_variance/fake.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/archive/conv_variance/fake.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/archive/conv_variance/fake.c	(revision 23352)
@@ -47,5 +47,5 @@
     psFree(weight);
 
-    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 0);
+    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS);
 
     psImage *bright = psImageAlloc(IMAGE_SIZE, IMAGE_SIZE, PS_TYPE_F32);
Index: /branches/cnb_branches/cnb_branch_20090301/archive/conv_variance/phot.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/archive/conv_variance/phot.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/archive/conv_variance/phot.c	(revision 23352)
@@ -52,5 +52,5 @@
 
     psStats *bgStats = psStatsAlloc(PS_STAT_ROBUST_MEDIAN);
-    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 0);
+    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS);
     psImageBackground(bgStats, NULL, image, mask, maskVal, rng);
     double bg = bgStats->robustMedian;
Index: /branches/cnb_branches/cnb_branch_20090301/dbconfig/changes.txt
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/dbconfig/changes.txt	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/dbconfig/changes.txt	(revision 23352)
@@ -795,6 +795,4 @@
 ALTER TABLE magicInputSkyfile DROP PRIMARY KEY, ADD PRIMARY KEY(magic_id, diff_id, node);
 
-
-
 -- Version: 1.1.48
 
@@ -834,4 +832,6 @@
     WHERE warpImfile.skycell_id IS NULL;
 
-
-
+-- Version: 1.1.49 (change detrend sequence)
+
+show create table detResidImfile;
+alter table detResidImfile drop foreign key detResidImfile_ibfk_3;
Index: /branches/cnb_branches/cnb_branch_20090301/dbconfig/config.md
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/dbconfig/config.md	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/dbconfig/config.md	(revision 23352)
@@ -2,4 +2,4 @@
     pkg_name        STR     ippdb
     pkg_namespace   STR     ippdb
-    pkg_version     STR     1.1.48
+    pkg_version     STR     1.1.49
 END
Index: /branches/cnb_branches/cnb_branch_20090301/dbconfig/det.md
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/dbconfig/det.md	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/dbconfig/det.md	(revision 23352)
@@ -90,14 +90,15 @@
     class_id    STR         64      # Primary Key
     uri         STR         255
-    recipe      STR         64
-    bg          F64         0.0
-    bg_stdev    F64         0.0
-    bg_mean_stdev   F64     0.0
-    user_1      F64         0.0
-    user_2      F64         0.0
-    user_3      F64         0.0
-    user_4      F64         0.0
-    user_5      F64         0.0
-#   XXX does it make sense to 'clean' the stacked imfiled?
+    # XXX missing path_base
+    recipe      STR         64
+    bg          F64         0.0
+    bg_stdev    F64         0.0
+    bg_mean_stdev   F64     0.0
+    user_1      F64         0.0
+    user_2      F64         0.0
+    user_3      F64         0.0
+    user_4      F64         0.0
+    user_5      F64         0.0
+    #   XXX does it make sense to 'clean' the stacked imfiled? (EAM: yes)
     data_state  STR         64      # full, cleaned, purged (only track end states; request states are in detRunSummary by iteration)
     fault       S16         0       # Key NOT NULL
@@ -148,18 +149,4 @@
     fault       S16         0       # Key NOT NULL
 END
-
-#detMasterFrame METADATA
-#    det_id      S64         0       # Primary Key
-#    iteration   S32         0       # Primary Key
-#    comment     STR         255
-#END
-#
-## drop?
-#detMasterImfile METADATA
-#    det_id      S64         0       # Primary Key
-#    class_id    STR         64      # Primary Key
-#    uri         STR         255
-#    recipe      STR         64
-#END
 
 detResidImfile METADATA
Index: /branches/cnb_branches/cnb_branch_20090301/dbconfig/dist.md
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/dbconfig/dist.md	(revision 23352)
+++ /branches/cnb_branches/cnb_branch_20090301/dbconfig/dist.md	(revision 23352)
@@ -0,0 +1,22 @@
+distRun METADATA
+    dist_id     S64         0       # Primary Key
+    stage       STR         64
+    stage_id    S64         0
+    chip_id     S64         0
+    label       STR         64      # Key
+    outroot     STR         255
+    clean       BOOL        f
+    state       STR         64      # Key
+    fault       S16         0
+END
+
+distComponent METADATA
+    dist_id     S64         0       # Primary Key fkey(dist_id) ref distRun(dist_id)
+    component   STR         64      # Key
+    bytes       S32         0
+    md5sum      STR         32
+    state       STR         64      # Key
+    name        STR         255
+    fault       S16         0
+END
+
Index: /branches/cnb_branches/cnb_branch_20090301/doc/misc/detnorm.rework.txt
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/doc/misc/detnorm.rework.txt	(revision 23352)
+++ /branches/cnb_branches/cnb_branch_20090301/doc/misc/detnorm.rework.txt	(revision 23352)
@@ -0,0 +1,76 @@
+
+I've been running the detrend analysis on the new gpc1 flats, and
+discovered an important issue.  These chips have significant gain
+variations across the devices.  
+
+We need to have detrend.normstats run on the results of the
+detResidImfiles, rather than detProcessedImfiles.  This requires
+altering the processing sequence from this:
+
+* process each of the input images (detrend.process.imfile)
+* generate a stack for each chip (detrend.stack.imfile)
+* use the stats from the processed chips to determine per-chip
+renormalizations for the stacks. the goal in this step is to yield a
+flat-field image for which a flattened input image is flat across the
+entire mosaic, not just each chip.  (detrend.normstats.imfile)
+* apply the normalization to each of the stacks (detrend.norm.imfile)
+* generate mosaic-wide stats and jpegs for the normalized flats
+(detrend.norm.exp)
+* apply the normalized flats to the processed images (detrend.resid.imfile)
+* generate per-exposure stats and jpegs for the residual images.
+(detrend.resid.exp)
+
+The problem is that, with a large gain variation across each chip, the
+measurement of the per-chip stats from the processed images is not a
+good measurement of the flattened mean value on that chip.  We really
+should be measuring the statistics from the flattened images.  Paul
+and I discussed the pipeline organization and have concluded that we
+can re-work the detrend pipeline without too much work to achieve this
+if we perform the flat-fielding before the normalization, and do the
+following steps:
+
+* process each of the input images (detrend.process.imfile)
+* generate a stack for each chip (detrend.stack.imfile)
+* apply the flats to the processed images (detrend.resid.imfile)
+* use the stats from the residual chips to determine per-chip
+renormalizations for the stacks (detrend.normstats.imfile)
+* apply the normalization to each of the stacks (detrend.norm.imfile)
+* generate mosaic-wide stats and jpegs for the normalized flats
+(detrend.norm.exp)
+* generate per-exposure stats and jpegs for the residual images,
+applying the normalizations on-the-fly. (detrend.resid.exp)
+
+--
+
+Tasks to do:
+
+* ppStack could do an approximate normalization on the fly (perhaps
+  not needed; perhaps already done? what is the output scaling of the
+  resulting flat stack?)
+
+* dettool -toresidimfile should trigger on completed detStackedImfile
+  (not detNormalizedImfile) (DONE)
+
+* dettool -tonormalizedstat should trigger on completed
+  detResidImfile, not detStackedImfile (DONE)
+
+* detrend_norm_calc.pl needs to read the stats from detResidImfile,
+  not detProcessedImfile (DONE)
+
+* detrend.norm.imfile uses the results of detrend_norm_calc.pl as
+  before (DONE)
+
+* detrend.resid.exp applies the normalizations on-the-fly to the b1
+  and b2 input images, and to the exposure-wide stats pushed into the
+  db. (DONE)
+
+* detrend.resid.exp blocks until detrend.norm.exp is done
+
+*** the 'verify' analysis needs to be modified.  I think we can keep
+    it as it was (detrend.process.imfile -> detrend.resid.imfile ->
+    detrend.resid.exp) where the resid imfile is generated using the
+    detNormalizedImfile rather than the detStackedImfile.  Does
+    detrend.resid.exp need to be modified for 'verify' mode to *avoid*
+    applying the normalization?
+
+
Index: /branches/cnb_branches/cnb_branch_20090301/doc/pslib/psLibSDRS.tex
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/doc/pslib/psLibSDRS.tex	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/doc/pslib/psLibSDRS.tex	(revision 23352)
@@ -1197,4 +1197,12 @@
 while returning the type involves descending through a case statement.
 
+To compare the types of two pointers without knowing their types, the following function may be used:
+
+\begin{prototype}
+bool psMemTypeEqual (void *ptr1, ///< pointer to first psMemory object
+		     void *ptr2 ///< pointer to second psMemory object
+  );
+\end{prototype}
+
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
Index: /branches/cnb_branches/cnb_branch_20090301/ippMonitor/def/detNormalizedStatImfile_failure.d
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippMonitor/def/detNormalizedStatImfile_failure.d	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippMonitor/def/detNormalizedStatImfile_failure.d	(revision 23352)
@@ -4,5 +4,5 @@
 MENU  ipp.detrend.dat
 
-where fault > 0
+WHERE fault > 0
 
 #     field    size  format  name   show   link to     extras
Index: /branches/cnb_branches/cnb_branch_20090301/ippMonitor/def/detStackedImfile.d
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippMonitor/def/detStackedImfile.d	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippMonitor/def/detStackedImfile.d	(revision 23352)
@@ -1,17 +1,27 @@
-TABLE detStackedImfile
+TABLE detStackedImfile, detRun
 TITLE detStackedImfile
 FILE  detStackedImfile.php
 MENU  ipp.imfiles.dat
 
-#     field         size  format  name                   show  link to     extras
-FIELD det_id,         7,  %s,    det_id
-FIELD iteration,      5,  %s,    iteration
-FIELD class_id,       8,  %s,    class_id
-FIELD fault,          5,  %d,    fault
-FIELD bg,             8,  %f,    backgnd
-FIELD bg_stdev,       8,  %f,    stdev
-FIELD bg_mean_stdev,  8,  %f,    [stdev]
-FIELD uri,           20,  %s,    uri
+WHERE detStackedImfile.det_id = detRun.det_id
+
+# XXX change uri to path_base in db
+ARGS  ARG1 detStackedImfile.det_id=$det_id
+ARGS  ARG1 detStackedImfile.iteration=$iteration
+ARGS  ARG1 camera=$detRun.camera
+ARGS  ARG1 basename=$detStackedImfile.uri
+
+#     field         size  format  name      show  link to     extras
+FIELD detStackedImfile.det_id,         7,  %s,    det_id
+FIELD detStackedImfile.iteration,      5,  %s,    iteration
+FIELD detStackedImfile.class_id,       8,  %s,    class_id,   value, detStackedImfile.php, ARG1
+FIELD detStackedImfile.fault,          5,  %d,    fault
+FIELD detStackedImfile.bg,             8,  %f,    backgnd
+FIELD detStackedImfile.bg_stdev,       8,  %f,    stdev
+FIELD detStackedImfile.bg_mean_stdev,  8,  %f,    [stdev]
+FIELD detStackedImfile.uri,           20,  %s,    uri
 # FIELD recipe, 20,   recipe
 
 TD_CLASS list_off $fault > 0
+
+TAIL PHP insert_log ('LOG.EXP');
Index: /branches/cnb_branches/cnb_branch_20090301/ippMonitor/raw/getimage.php
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippMonitor/raw/getimage.php	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippMonitor/raw/getimage.php	(revision 23352)
@@ -22,5 +22,9 @@
 putenv("PATH=$BINDIR:$PATH");
 
-# echo "args: $args<br>";
+if ($debug) {
+  echo "args: $args<br>";
+  echo "path: $PATH<br>";
+  echo "perl: $PERLLIB<br>";
+}
 
 # $basename may contain filename@filerule
@@ -31,6 +35,9 @@
 
 # $filerule = strtok("@");
-# echo "basename: $basename<br>";
-# echo "filerule: $filerule<br>";
+
+if ($debug) {
+  echo "basename: $basename<br>";
+  echo "filerule: $filerule<br>";
+}
 
 # need to supply the camera as well...
@@ -49,4 +56,14 @@
 $basename = escapeshellarg($basename);
 $basename = str_replace ('..','',$basename);
+
+if ($debug) {
+  exec ("which ipp_filename.pl", $output, $status);
+  echo "output:   $output[0]<br>";
+  echo "status:   $status<br>";
+
+  exec ("whoami", $output, $status);
+  echo "output:   $output[0]<br>";
+  echo "status:   $status<br>";
+}
 
 exec ("ipp_filename.pl --site=$SITE --basename $basename --filerule $filerule --camera $camera --class_id $class_id", $output, $status);
Index: /branches/cnb_branches/cnb_branch_20090301/ippScripts/Build.PL
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippScripts/Build.PL	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippScripts/Build.PL	(revision 23352)
@@ -80,4 +80,6 @@
         scripts/summit_copy.pl
         scripts/ipp_image_path.pl
+        scripts/dist_component.pl
+        scripts/dist_advancerun.pl
     )],
     dist_abstract => 'Scripts for running the Pan-STARRS IPP',
Index: /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/calibrate_dvo.pl
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/calibrate_dvo.pl	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/calibrate_dvo.pl	(revision 23352)
@@ -52,7 +52,7 @@
 my $caltool  = can_run('caltool')  or (warn "Can't find caltool"  and $missing_tools = 1);
 
-if ($missing_tools) { 
+if ($missing_tools) {
     warn ("Can't find required tools");
-    exit($PS_EXIT_CONFIG_ERROR); 
+    exit($PS_EXIT_CONFIG_ERROR);
 }
 
@@ -74,5 +74,5 @@
         cache_run(command => $command, verbose => 1);
 
-    unless ($success) { 
+    unless ($success) {
         $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
         &my_die ("Unable to perform addstar -resort on region $region: $error_code", $cal_id, $region, "RESORT", $status, $dbname);
@@ -83,15 +83,15 @@
 {
     foreach my $filter (@filters) {
-	my $command = "$relphot $filter";
-	$command .= "-D CATDIR $dvodb";
-	$command .= "-region $RAs $RAe $DECs $DECe";
+        my $command = "$relphot $filter";
+        $command .= "-D CATDIR $dvodb";
+        $command .= "-region $RAs $RAe $DECs $DECe";
 
-	my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
-	    cache_run(command => $command, verbose => 1);
+        my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+            cache_run(command => $command, verbose => 1);
 
-	unless ($success) { 
-	    $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
-	    &my_die ("Unable to perform addstar -resort on region $region: $error_code", $cal_id, $region, "RELPHOT", $status, $dbname);
-	}
+        unless ($success) {
+            $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+            &my_die ("Unable to perform addstar -resort on region $region: $error_code", $cal_id, $region, "RELPHOT", $status, $dbname);
+        }
     }
 }
@@ -101,15 +101,15 @@
 if (0) {
     foreach my $filter (@filters) {
-	my $command = "$uniphot $filter";
-	$command .= "-D CATDIR $dvodb";
-	$command .= "-region $RAs $RAe $DECs $DECe";
+        my $command = "$uniphot $filter";
+        $command .= "-D CATDIR $dvodb";
+        $command .= "-region $RAs $RAe $DECs $DECe";
 
-	my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
-	    cache_run(command => $command, verbose => 1);
+        my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+            cache_run(command => $command, verbose => 1);
 
-	unless ($success) { 
-	    $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
-	    &my_die ("Unable to perform addstar -resort on region $region: $error_code", $cal_id, $region, "UNIPHOT", $status, $dbname);
-	}
+        unless ($success) {
+            $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+            &my_die ("Unable to perform addstar -resort on region $region: $error_code", $cal_id, $region, "UNIPHOT", $status, $dbname);
+        }
     }
 }
@@ -121,9 +121,9 @@
 
     my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
-	cache_run(command => $command, verbose => 1);
+        cache_run(command => $command, verbose => 1);
 
-    unless ($success) { 
-	$error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
-	&my_die ("Unable to perform addstar -resort on region $region: $error_code", $cal_id, $region, "RELASTRO.OBJECTS", $status, $dbname);
+    unless ($success) {
+        $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+        &my_die ("Unable to perform addstar -resort on region $region: $error_code", $cal_id, $region, "RELASTRO.OBJECTS", $status, $dbname);
     }
 }
@@ -135,9 +135,9 @@
 
     my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
-	cache_run(command => $command, verbose => 1);
+        cache_run(command => $command, verbose => 1);
 
-    unless ($success) { 
-	$error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
-	&my_die ("Unable to perform addstar -resort on region $region: $error_code", $cal_id, $region, "RELASTRO.IMAGES", $status, $dbname);
+    unless ($success) {
+        $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+        &my_die ("Unable to perform addstar -resort on region $region: $error_code", $cal_id, $region, "RELASTRO.IMAGES", $status, $dbname);
     }
 }
@@ -169,14 +169,14 @@
     my $region    = shift;
     my $last_step = shift;
-    my $status 	  = shift;
-    my $dbname 	  = shift;
+    my $status    = shift;
+    my $dbname    = shift;
 
     carp($msg);
     if (defined $cal_id && defined $region && defined $last_step && defined $status and not $no_update) {
         my $command = "$caltool -addcalrun";
-	$command .= " -cal_id $cal_id";
+        $command .= " -cal_id $cal_id";
         $command .= " -region $region";
-	$command .= " -last_step $last_step";
-	$command .= " -state $status";
+        $command .= " -last_step $last_step";
+        $command .= " -state $status";
         $command .= " -dbname $dbname" if defined $dbname;
         system ($command);
Index: /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/camera_exp.pl
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/camera_exp.pl	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/camera_exp.pl	(revision 23352)
@@ -22,6 +22,4 @@
 use PS::IPP::Config 1.01 qw( :standard );
 use File::Temp qw( tempfile );
-
-my $ipprc = PS::IPP::Config->new(); # IPP configuration
 
 use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
@@ -70,9 +68,5 @@
     defined $camera;
 
-# Unhandled exceptions should be passed on to my_die so they get pushed into the database
-$SIG{__DIE__} = sub { die @_ if $^S;
-                      my_die( $_[0], $cam_id, $PS_EXIT_UNKNOWN_ERROR ); };
-
-$ipprc->define_camera($camera);
+my $ipprc = PS::IPP::Config->new( $camera ) or my_die( "Unable to set up", $cam_id, $PS_EXIT_CONFIG_ERROR ); # IPP configuration
 
 my $logDest = $ipprc->filename("LOG.EXP", $outroot) or &my_die("Missing entry from camera config", $cam_id, $PS_EXIT_CONFIG_ERROR);
@@ -84,5 +78,5 @@
 
 if ($redirect) {
-    $ipprc->redirect_output($logDest);
+    $ipprc->redirect_output($logDest) or my_die( "Unable to redirect output", $cam_id, $PS_EXIT_SYS_ERROR );
     print "\n\n";
     print "Starting script $0 on $host\n\n";
@@ -372,4 +366,6 @@
     my $cam_id = shift; # Camtool identifier
     my $exit_code = shift; # Exit code to add
+
+    $exit_code = $PS_EXIT_PROG_ERROR unless defined $exit_code;
 
     carp($msg);
Index: /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/chip_imfile.pl
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/chip_imfile.pl	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/chip_imfile.pl	(revision 23352)
@@ -20,6 +20,4 @@
 use PS::IPP::Metadata::Config;
 use PS::IPP::Config 1.01 qw( :standard );
-
-my $ipprc = PS::IPP::Config->new(); # IPP configuration
 
 use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
@@ -70,9 +68,5 @@
     defined $run_state;
 
-# Unhandled exceptions should be passed on to my_die so they get pushed into the database
-$SIG{__DIE__} = sub { die @_ if $^S;
-                      my_die( $_[0], $exp_id, $chip_id, $class_id, $PS_EXIT_UNKNOWN_ERROR ); };
-
-$ipprc->define_camera($camera);
+my $ipprc = PS::IPP::Config->new( $camera ) or my_die( "Unable to set up", $exp_id, $chip_id, $class_id, $PS_EXIT_CONFIG_ERROR ); # IPP configuration
 
 my $logDest = $ipprc->filename("LOG.IMFILE", $outroot, $class_id) or &my_die("Missing entry from camera config", $exp_id, $chip_id, $class_id, $PS_EXIT_CONFIG_ERROR);
@@ -80,5 +74,5 @@
 
 if ($redirect) {
-    $ipprc->redirect_output($logDest);
+    $ipprc->redirect_output($logDest) or my_die( "Unable to redirect output", $exp_id, $chip_id, $class_id, $PS_EXIT_SYS_ERROR );
     print STDOUT "\n\n";
     print STDOUT "Starting script $0 on $host\n\n";
@@ -254,4 +248,6 @@
     # run_state, outputImage, and outroot are globals
 
+    $exit_code = $PS_EXIT_PROG_ERROR unless defined $exit_code;
+
     carp($msg);
     if (defined $chip_id and defined $class_id and not $no_update) {
Index: /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/detrend_correct_imfile.pl
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/detrend_correct_imfile.pl	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/detrend_correct_imfile.pl	(revision 23352)
@@ -18,6 +18,4 @@
 use PS::IPP::Metadata::Config;
 use PS::IPP::Config 1.01 qw( :standard );
-
-my $ipprc = PS::IPP::Config->new(); # IPP configuration
 
 use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
@@ -60,15 +58,9 @@
     and defined $camera;
 
-# Unhandled exceptions should be passed on to my_die so they get pushed into the database
-$SIG{__DIE__} = sub { die @_ if $^S;
-                      my_die( $_[0], $det_id, $class_id, $PS_EXIT_UNKNOWN_ERROR ); };
-
-# XXX this exits with status = 0 on failure
-$ipprc->define_camera($camera);
-
+my $ipprc = PS::IPP::Config->new( $camera ) or my_die( "Unable to set up", $det_id, $class_id, $PS_EXIT_CONFIG_ERROR ); # IPP configuration
 if ($redirect) {
     my $logDest = $ipprc->filename("LOG.IMFILE", $outroot, $class_id)
        or &my_die("Missing entry from camera config", $det_id, $class_id, $PS_EXIT_CONFIG_ERROR);
-    $ipprc->redirect_output($logDest);
+    $ipprc->redirect_output($logDest) or my_die( "Unable to redirect output", $det_id, $class_id, $PS_EXIT_SYS_ERROR );
 }
 
@@ -132,4 +124,6 @@
     my $exit_code = shift; # Exit code to add
 
+    $exit_code = $PS_EXIT_PROG_ERROR unless defined $exit_code;
+
     carp($msg);
     if (defined $det_id and defined $class_id and not $no_update) {
Index: /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/detrend_norm_apply.pl
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/detrend_norm_apply.pl	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/detrend_norm_apply.pl	(revision 23352)
@@ -17,6 +17,4 @@
 use PS::IPP::Metadata::Config;
 use PS::IPP::Config 1.01 qw( :standard );
-
-my $ipprc = PS::IPP::Config->new(); # IPP configuration
 
 use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
@@ -40,5 +38,5 @@
     'iteration|n=s'     => \$iter,       # Iteration
     'class_id|i=s'      => \$class_id,   # Class ID
-    'value|v=s'         => \$value,      # Value to multiple (for normalisation)
+    'value|v=s'         => \$value,      # Value to apply (for normalisation)
     'input_uri|u=s'     => \$input_uri,  # Input file
     'camera|c=s'        => \$camera,     # Camera
@@ -64,13 +62,9 @@
     defined $outroot;
 
-# Unhandled exceptions should be passed on to my_die so they get pushed into the database
-$SIG{__DIE__} = sub { die @_ if $^S;
-                      my_die( $_[0], $det_id, $iter, $class_id, $PS_EXIT_UNKNOWN_ERROR ); };
-
-$ipprc->define_camera($camera);
+my $ipprc = PS::IPP::Config->new( $camera ) or my_die( "Unable to set up", $det_id, $iter, $class_id, $PS_EXIT_CONFIG_ERROR ); # IPP configuration
 
 my $logDest = $ipprc->filename("LOG.IMFILE", $outroot, $class_id)
         or &my_die("Missing entry from camera config", $det_id, $iter, $class_id, $PS_EXIT_CONFIG_ERROR);
-$ipprc->redirect_output($logDest) if $redirect;
+$ipprc->redirect_output($logDest) or my_die( "Unable to redirect output", $det_id, $iter, $class_id, $PS_EXIT_SYS_ERROR ) if $redirect;
 
 my $RECIPE_PPIMAGE = 'PPIMAGE_N'; # Recipe to use with ppImage
@@ -202,4 +196,6 @@
     my $exit_code = shift;      # Exit code to add
 
+    $exit_code = $PS_EXIT_PROG_ERROR unless defined $exit_code;
+
     carp($msg);
     if (defined $det_id and defined $iter and defined $class_id and not $no_update) {
Index: /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/detrend_norm_calc.pl
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/detrend_norm_calc.pl	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/detrend_norm_calc.pl	(revision 23352)
@@ -19,6 +19,4 @@
 use PS::IPP::Metadata::List qw( parse_md_list );
 use PS::IPP::Config 1.01 qw( :standard );
-
-my $ipprc = PS::IPP::Config->new(); # IPP configuration
 
 use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
@@ -57,11 +55,8 @@
     defined $outroot;
 
-# Unhandled exceptions should be passed on to my_die so they get pushed into the database
-$SIG{__DIE__} = sub { die @_ if $^S;
-                      my_die( $_[0], $det_id, $iter, $PS_EXIT_UNKNOWN_ERROR ); };
+my $ipprc = PS::IPP::Config->new() or my_die( "Unable to set up", $det_id, $iter, $PS_EXIT_CONFIG_ERROR ); # IPP configuration
 
 my $logfile = $outroot . ".log";
-
-$ipprc->redirect_output($logfile) if $redirect;
+$ipprc->redirect_output($logfile) or my_die( "Unable to redirect output", $det_id, $iter, $PS_EXIT_SYS_ERROR ) if $redirect;
 
 use constant STATISTIC => 'bg'; # Background statistic to use from the database
@@ -95,6 +90,7 @@
 my @files;                      # The input files
 {
-    my $command = "$dettool -processedimfile";
-    $command .= " -det_id $det_id"; # Command to run
+    my $command = "$dettool -residimfile";
+    $command .= " -det_id $det_id";
+    $command .= " -iteration $iter";
     $command .= " -included"; # only use the inputs for this detrend run to calculate the norm
     $command .= " -dbname $dbname" if defined $dbname;
@@ -103,5 +99,5 @@
     print "Running [$command]...\n" if $verbose;
     if (not run(\@command, \$stdin, \$stdout, \$stderr)) {
-        &my_die("Unable to perform dettool -processedimfile on detrend $det_id/$iter: $?",
+        &my_die("Unable to perform dettool -residimfile on detrend $det_id/$iter: $?",
                 $det_id, $iter, $PS_EXIT_SYS_ERROR);
     }
@@ -233,4 +229,6 @@
     my $iter = shift;           # Iteration
     my $exit_code = shift;      # Exit code to add
+
+    $exit_code = $PS_EXIT_PROG_ERROR unless defined $exit_code;
 
     carp($msg);
Index: /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/detrend_norm_exp.pl
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/detrend_norm_exp.pl	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/detrend_norm_exp.pl	(revision 23352)
@@ -19,6 +19,4 @@
 use PS::IPP::Metadata::List qw( parse_md_list );
 use File::Temp qw( tempfile );
-
-my $ipprc = PS::IPP::Config->new(); # IPP configuration
 
 use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
@@ -60,13 +58,7 @@
     defined $outroot;
 
-# Unhandled exceptions should be passed on to my_die so they get pushed into the database
-$SIG{__DIE__} = sub { die @_ if $^S;
-                      my_die( $_[0], $det_id, $iter, $PS_EXIT_UNKNOWN_ERROR ); };
-
+my $ipprc = PS::IPP::Config->new( $camera ) or my_die( "Unable to set up", $det_id, $iter, $PS_EXIT_CONFIG_ERROR ); # IPP configuration
 my $logfile = $outroot . ".log";
-
-$ipprc->redirect_output($logfile) if $redirect;
-
-$ipprc->define_camera($camera);
+$ipprc->redirect_output($logfile) or my_die( "Unable to redirect output", $det_id, $iter, $PS_EXIT_SYS_ERROR ) if $redirect;
 
 # Recipes to use based on reduction class
@@ -190,4 +182,6 @@
     my $iter = shift;           # Iteration
     my $exit_code = shift; # Exit code to add
+
+    $exit_code = $PS_EXIT_PROG_ERROR unless defined $exit_code;
 
     carp($msg);
Index: /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/detrend_process_exp.pl
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/detrend_process_exp.pl	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/detrend_process_exp.pl	(revision 23352)
@@ -19,6 +19,4 @@
 use PS::IPP::Metadata::List qw( parse_md_list );
 use File::Temp qw( tempfile );
-
-my $ipprc = PS::IPP::Config->new(); # IPP configuration
 
 use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
@@ -63,14 +61,9 @@
     defined $outroot;
 
-# Unhandled exceptions should be passed on to my_die so they get pushed into the database
-$SIG{__DIE__} = sub { die @_ if $^S;
-                      my_die( $_[0], $det_id, $exp_id, $PS_EXIT_UNKNOWN_ERROR ); };
-
-$ipprc->define_camera($camera);
-
+my $ipprc = PS::IPP::Config->new( $camera ) or my_die( "Unable to set up", $det_id, $exp_id, $PS_EXIT_CONFIG_ERROR ); # IPP configuration
 if ($redirect) {
     my $logDest = $ipprc->filename("LOG.EXP", $outroot, "NONE")
         or &my_die("Missing entry in file rules", $det_id, $exp_id, $PS_EXIT_CONFIG_ERROR);
-    $ipprc->redirect_output($logDest);
+    $ipprc->redirect_output($logDest) or my_die( "Unable to redirect output", $det_id, $exp_id, $PS_EXIT_SYS_ERROR );
 }
 
@@ -204,4 +197,6 @@
     my $exp_id = shift; # Exposure tag
     my $exit_code = shift; # Exit code to add
+
+    $exit_code = $PS_EXIT_PROG_ERROR unless defined $exit_code;
 
     carp($msg);
Index: /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/detrend_process_imfile.pl
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/detrend_process_imfile.pl	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/detrend_process_imfile.pl	(revision 23352)
@@ -17,6 +17,4 @@
 use PS::IPP::Metadata::Config;
 use PS::IPP::Config 1.01 qw( :standard );
-
-my $ipprc = PS::IPP::Config->new(); # IPP configuration
 
 use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
@@ -65,14 +63,7 @@
     defined $outroot;
 
-# Unhandled exceptions should be passed on to my_die so they get pushed into the database
-$SIG{__DIE__} = sub { die @_ if $^S;
-                      my_die( $_[0], $det_id, $exp_id, $class_id, $PS_EXIT_UNKNOWN_ERROR ); };
-
-# XXX this exits with status = 0 on failure
-$ipprc->define_camera($camera);
-
+my $ipprc = PS::IPP::Config->new( $camera ) or my_die( "Unable to set up", $det_id, $exp_id, $class_id, $PS_EXIT_CONFIG_ERROR ); # IPP configuration
 my $logDest = $ipprc->filename("LOG.IMFILE", $outroot, $class_id) or &my_die("Missing entry from camera config", $det_id, $exp_id, $class_id, $PS_EXIT_CONFIG_ERROR);
-
-$ipprc->redirect_output($logDest) if $redirect;
+$ipprc->redirect_output($logDest) or my_die( "Unable to redirect output", $det_id, $exp_id, $class_id, $PS_EXIT_SYS_ERROR ) if $redirect;
 
 # Recipes to use as a function of detrend type
@@ -81,4 +72,24 @@
 my $jpeg_recipe = $ipprc->reduction($reduction, uc($det_type) . '_JPEG_IMAGE'); # Recipe name for JPEG
 
+# The output file rule name depends on the detrend type
+my $FILERULES = { 'FLATMASK'         => undef,
+                  'DARKMASK'         => undef,
+                  'MASK'             => undef,
+                  'BIAS'             => undef,
+                  'DARK'             => undef,
+                  'DARK_PREMASK'     => undef,
+                  'SHUTTER'          => 'PPIMAGE.OUTPUT.DETREND',
+                  'FLAT_PREMASK'     => 'PPIMAGE.OUTPUT.DETREND',
+                  'DOMEFLAT_PREMASK' => 'PPIMAGE.OUTPUT.DETREND',
+                  'SKYFLAT_PREMASK'  => 'PPIMAGE.OUTPUT.DETREND',
+                  'FLAT_RAW'         => 'PPIMAGE.OUTPUT.DETREND',
+                  'DOMEFLAT_RAW'     => 'PPIMAGE.OUTPUT.DETREND',
+                  'SKYFLAT_RAW'      => 'PPIMAGE.OUTPUT.DETREND',
+                  'FLAT'             => 'PPIMAGE.OUTPUT.DETREND',
+                  'DOMEFLAT'         => 'PPIMAGE.OUTPUT.DETREND',
+                  'SKYFLAT'          => 'PPIMAGE.OUTPUT.DETREND',
+                  'FRINGE'           => undef,
+              };
+
 &my_die("Couldn't find input file: $input_uri\n", $det_id, $exp_id, $class_id, $PS_EXIT_SYS_ERROR) unless $ipprc->file_exists($input_uri);
 
@@ -93,9 +104,14 @@
 my $cmdflags;
 
-my $outputImage = $ipprc->filename("PPIMAGE.OUTPUT", $outroot, $class_id) or &my_die("Missing entry from camera config", $det_id, $exp_id, $class_id, $PS_EXIT_PROG_ERROR);
+my $filerule = $FILERULES->{$det_type}; # File rule to use for output
+$filerule =  "PPIMAGE.OUTPUT" unless defined $filerule;
+
+my $outputImage = $ipprc->filename($filerule,        $outroot, $class_id) or &my_die("Missing entry from camera config", $det_id, $exp_id, $class_id, $PS_EXIT_PROG_ERROR);
 my $outputBin1  = $ipprc->filename("PPIMAGE.BIN1",   $outroot, $class_id) or &my_die("Missing entry from camera config", $det_id, $exp_id, $class_id, $PS_EXIT_PROG_ERROR);
 my $outputBin2  = $ipprc->filename("PPIMAGE.BIN2",   $outroot, $class_id) or &my_die("Missing entry from camera config", $det_id, $exp_id, $class_id, $PS_EXIT_PROG_ERROR);
 my $outputStats = $ipprc->filename("PPIMAGE.STATS",  $outroot, $class_id) or &my_die("Missing entry from camera config", $det_id, $exp_id, $class_id, $PS_EXIT_PROG_ERROR);
 my $traceDest   = $ipprc->filename("TRACE.IMFILE",   $outroot, $class_id) or &my_die("Missing entry from camera config", $exp_id, $exp_id, $class_id, $PS_EXIT_CONFIG_ERROR);
+
+
 # Run ppImage
 unless ($no_op) {
@@ -105,4 +121,5 @@
     $command .= " -recipe PPSTATS DETSTATS";
     $command .= " -stats $outputStats";
+    $command .= " -F PPIMAGE.OUTPUT $filerule" if $filerule ne "PPIMAGE.OUTPUT";
     $command .= " -tracedest $traceDest -log $logDest";
     $command .= " -threads $threads" if defined $threads;
@@ -167,4 +184,6 @@
     my $class_id = shift; # Class identifier
     my $exit_code = shift; # Exit code to add
+
+    $exit_code = $PS_EXIT_PROG_ERROR unless defined $exit_code;
 
     carp($msg);
Index: /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/detrend_reject_exp.pl
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/detrend_reject_exp.pl	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/detrend_reject_exp.pl	(revision 23352)
@@ -20,6 +20,4 @@
 use PS::IPP::Metadata::List qw( parse_md_list );
 use Statistics::Descriptive;
-
-my $ipprc = PS::IPP::Config->new(); # IPP configuration
 
 my $ITER_LIMIT = 20;
@@ -60,14 +58,8 @@
     defined $outroot;
 
-# Unhandled exceptions should be passed on to my_die so they get pushed into the database
-$SIG{__DIE__} = sub { die @_ if $^S;
-                      my_die( $_[0], $det_id, $iter, $PS_EXIT_UNKNOWN_ERROR ); };
-
-# check for existing directory, generate if needed
-$ipprc->outroot_prepare($outroot);
-
+my $ipprc = PS::IPP::Config->new() or my_die( "Unable to set up", $det_id, $iter, $PS_EXIT_CONFIG_ERROR ); # IPP configuration
+$ipprc->outroot_prepare($outroot) or my_die( "Unable to prepare output root", $det_id, $iter, $PS_EXIT_SYS_ERROR );
 my $logName = "$outroot.log"; # Name for log
-
-$ipprc->redirect_output($logName) if $redirect;
+$ipprc->redirect_output($logName) or my_die( "Unable to redirect", $det_id, $iter, $PS_EXIT_SYS_ERROR ) if $redirect;
 
 # values to extract from output metadata and the stats to calculate
@@ -344,4 +336,6 @@
     my $exit_code = shift; # Exit code to add
 
+    $exit_code = $PS_EXIT_PROG_ERROR unless defined $exit_code;
+
     carp($msg);
     if (defined $det_id and defined $iter and not $no_update) {
Index: /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/detrend_resid_exp.pl
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/detrend_resid_exp.pl	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/detrend_resid_exp.pl	(revision 23352)
@@ -30,6 +30,4 @@
 use File::Temp qw( tempfile );                   # tools to construct temp files
 use PS::IPP::Config 1.01 qw( :standard );
-
-my $ipprc = PS::IPP::Config->new(); # IPP configuration
 
 use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt ); # option parsing
@@ -47,5 +45,5 @@
 
 # parse the command-line options
-my ( $det_id, $iter, $exp_id, $exp_tag, $det_type, $camera, $filter, $reject, $outroot, $dbname, $reduction,
+my ( $det_id, $iter, $exp_id, $exp_tag, $det_mode, $det_type, $camera, $filter, $reject, $outroot, $dbname, $reduction,
      $verbose, $no_update, $no_op, $save_temps, $redirect );
 GetOptions(
@@ -55,4 +53,5 @@
            'exp_tag|=s'        => \$exp_tag,
            'det_type|t=s'      => \$det_type,
+           'det_mode=s'        => \$det_mode,
            'camera=s'          => \$camera,
            'outroot|w=s'       => \$outroot,   # output file base name
@@ -76,17 +75,12 @@
     defined $exp_tag  and
     defined $det_type and
+    defined $det_mode and
     defined $camera   and
     defined $outroot;
 
-# Unhandled exceptions should be passed on to my_die so they get pushed into the database
-$SIG{__DIE__} = sub { die @_ if $^S;
-                      my_die( $_[0], $det_id, $iter, $exp_id, $PS_EXIT_UNKNOWN_ERROR ); };
-
 # load IPP config information for the specified camera
-$ipprc->define_camera($camera);
-
+my $ipprc = PS::IPP::Config->new( $camera ) or my_die( "Unable to set up", $det_id, $iter, $exp_id, $PS_EXIT_CONFIG_ERROR ); # IPP configuration
 my $logDest = $ipprc->filename("LOG.EXP", $outroot) or &my_die("Missing entry from camera config", $det_id, $iter, $exp_id, $PS_EXIT_CONFIG_ERROR);
-
-$ipprc->redirect_output($logDest) if $redirect;
+$ipprc->redirect_output($logDest) or my_die( "Unable to redirect output", $det_id, $iter, $exp_id, $PS_EXIT_SYS_ERROR ) if $redirect;
 
 # Recipes to use based on reduction class
@@ -96,7 +90,54 @@
 &my_die("Unrecognised detrend type: $det_type", $det_id, $iter, $PS_EXIT_PROG_ERROR) unless defined $recipe;
 
+# variables used for I/O
+my ($command, $success, $error_code, $full_buf, $stdout_buf, $stderr_buf);
+
+# Get list of normalizations by class_id : stored as $norms; save to temp file for ppImage runs below
+my (%norms, $normsName);
+if ($det_mode eq 'master') {
+    # dettool command to select imfile data for this exp_id
+    $command  = "$dettool -normalizedstat";
+    $command .= " -det_id $det_id";
+    $command .= " -iteration $iter";
+    $command .= " -dbname $dbname" if defined $dbname;
+    ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => $verbose);
+    unless ($success) {
+        $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+        warn("Unable to perform dettool -residimfile: $error_code\n");
+        exit($error_code);
+    }
+    if (@$stdout_buf == 0) {
+        &my_die("No normalizations were found", $det_id, $iter, $PS_EXIT_PROG_ERROR);
+    }
+
+    # Parse the stdout buffer into a metadata
+    my $mdcParser = PS::IPP::Metadata::Config->new;
+    my $metadata = $mdcParser->parse(join "", @$stdout_buf) or
+        &my_die("Unable to parse metadata config doc", $det_id, $iter, $exp_id, $PS_EXIT_PROG_ERROR);
+
+    # parse the file info in the metadata
+    my $normsMD = parse_md_list($metadata) or
+        &my_die("Unable to parse metadata list", $det_id, $iter, $exp_id, $PS_EXIT_PROG_ERROR);
+
+
+    # write the normalizations to a file as a metadata config file in the form: class_id F32 value
+    # XXX a possible optimization: if there is only one imfile, skip normalization
+    my $normsFile;
+    ($normsFile, $normsName) = tempfile( "/tmp/$exp_tag.detresid.$det_id.$iter.norms.XXXX", UNLINK => !$save_temps );
+    print "saving norms to $normsName\n";
+    foreach my $norm (@$normsMD) {
+        my $class_id = $norm->{class_id};
+        my $normalization = $norm->{norm};
+
+        $norms{$class_id} = $normalization;
+        printf $normsFile "$class_id F32 $normalization\n",
+    }
+    close $normsFile;
+}
+
 # Get list of imfile files
 my $cmdflags;
-my ($files, $command, $success, $error_code, $full_buf, $stdout_buf, $stderr_buf);
+my (@files);
 {
     # dettool command to select imfile data for this exp_id
@@ -113,5 +154,7 @@
         exit($error_code);
     }
-    # XXX report an error message if stdout_buf is empty
+    if (@$stdout_buf == 0) {
+        &my_die("No imfiles were found", $det_id, $iter, $PS_EXIT_PROG_ERROR);
+    }
 
     # Parse the stdout buffer into a metadata
@@ -120,13 +163,52 @@
         &my_die("Unable to parse metadata config doc", $det_id, $iter, $exp_id, $PS_EXIT_PROG_ERROR);
 
+    # since I can't figure out how to do input and output within PERL, I'm writing the (modified) metadata to a temp file
+    my ($statFile, $statName) = tempfile( "/tmp/$exp_tag.detresid.$det_id.$iter.stats.XXXX", UNLINK => !$save_temps );
+
+    print $statFile "rawResidImfile MULTI\n";
+
     # parse the file info in the metadata
-    $files = parse_md_list($metadata) or
-        &my_die("Unable to parse metadata list", $det_id, $iter, $exp_id, $PS_EXIT_PROG_ERROR);
-
-    # since I can't figure out how to do input and output within PERL, I'm writing to a temp file
-    my ($statFile, $statName) = tempfile( "/tmp/$exp_tag.detresid.$det_id.$iter.stats.XXXX", UNLINK => !$save_temps );
-    print "saving stats to $statName\n";
-    foreach my $line (@$stdout_buf) {
-        print $statFile $line;
+    # as we parse the list of files and their stats, apply the normalization to the relevant fields
+    # also, write out the modified metadata set
+    foreach my $mdItem (@$metadata) {
+        if ($mdItem->{class} ne "metadata") {
+            carp "MD element ", $mdItem->{name}, " isn't of type METADATA --- ignored.\n";
+            next;
+        }
+        my %hash;               # Hash element
+        my $mdComponents = $mdItem->{value}; # Components of the metadata
+
+        # determine the class_id for this block:
+        my $class_id;
+        foreach my $data (@$mdComponents) {
+            unless ($data->{name} eq "class_id") { next; }
+            $class_id = $data->{value};
+            last;
+        }
+
+        # a new metadata block
+        print $statFile "rawResidImfile  METADATA\n";
+
+        # modify and save the data in this block:
+        foreach my $data (@$mdComponents) {
+	    my $norm = 1.0;
+	    if ($det_mode eq "master") {
+		$norm = $norms{$class_id};
+	    }
+
+            # fields to modify by the normalization:
+            if ($data->{name} eq "bg")            { $data->{value} *= $norm; }
+            if ($data->{name} eq "bg_stdev")      { $data->{value} *= $norm; }
+            if ($data->{name} eq "bg_mean_stdev") { $data->{value} *= $norm; }
+            if ($data->{name} eq "bg_skewness")   { $data->{value} *= $norm; }
+            if ($data->{name} eq "bg_kurtosis")   { $data->{value} *= $norm; }
+            if ($data->{name} eq "bin_stdev")     { $data->{value} *= $norm; }
+
+            # write out the metadata, save on the array of hashes
+            print $statFile "  $data->{name}  $data->{type}  $data->{value}\n";
+            $hash{$data->{name}} = $data->{value};
+        }
+        print $statFile "END\n";
+        push @files, \%hash;
     }
     close $statFile;
@@ -162,5 +244,5 @@
 my ($list1File, $list1Name) = tempfile( "/tmp/$exp_tag.detresid.$det_id.$iter.b1.list.XXXX", UNLINK => !$save_temps );
 my ($list2File, $list2Name) = tempfile( "/tmp/$exp_tag.detresid.$det_id.$iter.b2.list.XXXX", UNLINK => !$save_temps );
-foreach my $file (@$files) {
+foreach my $file (@files) {
     print $list1File ($ipprc->filename( "PPIMAGE.BIN1", $file->{path_base}, $file->{class_id} ) . "\n");
     print $list2File ($ipprc->filename( "PPIMAGE.BIN2", $file->{path_base}, $file->{class_id} ) . "\n");
@@ -173,7 +255,9 @@
 unless ($no_op) {
     # Make the jpeg for binning 1
+    # XXX EAM : supply the collection of normalizations as a metadata
     $command = "$ppImage -list $list1Name $outroot"; # Command to run
     $command .= " -recipe PPIMAGE PPIMAGE_J1";
     $command .= " -recipe JPEG $recipe";
+    $command .= " -normlist $normsName" if defined $normsName;
     $command .= " -dbname $dbname" if defined $dbname;
     ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
@@ -186,7 +270,9 @@
 
     # Make the jpeg for binning 2
+    # XXX EAM : supply the collection of normalizations as a metadata
     $command = "$ppImage -list $list2Name $outroot"; # Command to run
     $command .= " -recipe PPIMAGE PPIMAGE_J2";
     $command .= " -recipe JPEG $recipe";
+    $command .= " -normlist $normsName" if defined $normsName;
     $command .= " -dbname $dbname" if defined $dbname;
     ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
@@ -223,5 +309,5 @@
 my @fluxes;
 
-foreach my $file (@$files) {
+foreach my $file (@files) {
     my $name      = $file->{class_id};
     my $mean      = $file->{bg};        # Mean for this imfile
@@ -554,4 +640,6 @@
     my $exit_code = shift; # Exit code to add
 
+    $exit_code = $PS_EXIT_PROG_ERROR unless defined $exit_code;
+
     carp($msg);
     if (defined $det_id and defined $iter and defined $exp_id and not $no_update) {
Index: /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/detrend_resid_imfile.pl
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/detrend_resid_imfile.pl	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/detrend_resid_imfile.pl	(revision 23352)
@@ -17,6 +17,4 @@
 use PS::IPP::Metadata::Config;
 use PS::IPP::Config 1.01 qw( :standard );
-
-my $ipprc = PS::IPP::Config->new(); # IPP configuration
 
 use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
@@ -76,13 +74,7 @@
     defined $detrend;
 
-# Unhandled exceptions should be passed on to my_die so they get pushed into the database
-$SIG{__DIE__} = sub { die @_ if $^S;
-                      my_die( $_[0], $det_id, $iter, $exp_id, $class_id, $PS_EXIT_UNKNOWN_ERROR ); };
-
-$ipprc->define_camera($camera);
-my $logDest     = $ipprc->filename("LOG.IMFILE", $outroot, $class_id);
-if ($redirect) {
-    $ipprc->redirect_output($logDest);
-}
+my $ipprc = PS::IPP::Config->new( $camera ) or my_die( "Unable to set up", $det_id, $iter, $exp_id, $class_id, $PS_EXIT_CONFIG_ERROR ); # IPP configuration
+my $logDest = $ipprc->filename("LOG.IMFILE", $outroot, $class_id) or my_die( "Unable to find LOG.IMFILE", $det_id, $iter, $exp_id, $class_id, $PS_EXIT_CONFIG_ERROR );
+$ipprc->redirect_output($logDest) or my_die( "Unable to find LOG.IMFILE", $det_id, $iter, $exp_id, $class_id, $PS_EXIT_SYS_ERROR ) if $redirect;
 
 # Recipes to use as a function of detrend type and mode
@@ -124,4 +116,24 @@
 };
 
+# The output file rule name depends on the detrend type
+my $FILERULES = { 'FLATMASK'         => 'PPIMAGE.OUTPUT.RESID',
+                  'DARKMASK'         => 'PPIMAGE.OUTPUT.RESID',
+                  'MASK'             => 'PPIMAGE.OUTPUT.RESID',
+                  'BIAS'             => 'PPIMAGE.OUTPUT.RESID',
+                  'DARK'             => 'PPIMAGE.OUTPUT.RESID',
+                  'DARK_PREMASK'     => 'PPIMAGE.OUTPUT.RESID',
+                  'SHUTTER'          => 'PPIMAGE.OUTPUT.DETREND',
+                  'FLAT_PREMASK'     => 'PPIMAGE.OUTPUT.DETREND',
+                  'DOMEFLAT_PREMASK' => 'PPIMAGE.OUTPUT.DETREND',
+                  'SKYFLAT_PREMASK'  => 'PPIMAGE.OUTPUT.DETREND',
+                  'FLAT_RAW'         => 'PPIMAGE.OUTPUT.DETREND',
+                  'DOMEFLAT_RAW'     => 'PPIMAGE.OUTPUT.DETREND',
+                  'SKYFLAT_RAW'      => 'PPIMAGE.OUTPUT.DETREND',
+                  'FLAT'             => 'PPIMAGE.OUTPUT.DETREND',
+                  'DOMEFLAT'         => 'PPIMAGE.OUTPUT.DETREND',
+                  'SKYFLAT'          => 'PPIMAGE.OUTPUT.DETREND',
+                  'FRINGE'           => 'PPIMAGE.OUTPUT.RESID',
+              };
+
 # outroot examples (HOST components must be set)
 # file://data/ipp004.0/gpc1/20080130
@@ -135,5 +147,7 @@
 # my $outputName  = $ipprc->filename("PPIMAGE.OUTPUT", $outroot, $class_id);
 
-my $outputName  = $ipprc->filename("PPIMAGE.OUTPUT.RESID", $outroot, $class_id);
+my $filerule = $FILERULES->{$det_type}; # File rule to use
+
+my $outputName  = $ipprc->filename($filerule,        $outroot, $class_id);
 my $bin1Name    = $ipprc->filename("PPIMAGE.BIN1",   $outroot, $class_id);
 my $bin2Name    = $ipprc->filename("PPIMAGE.BIN2",   $outroot, $class_id);
@@ -149,5 +163,5 @@
     $command .= " -recipe JPEG $jpeg_recipe";
     $command .= " -recipe PPSTATS RESIDUAL";
-    $command .= " -F PPIMAGE.OUTPUT PPIMAGE.OUTPUT.RESID";
+    $command .= " -F PPIMAGE.OUTPUT $filerule";
     $command .= " -stats $outputStats";
     $command .= " -tracedest $traceDest -log $logDest";
@@ -237,4 +251,6 @@
     my $exit_code = shift; # Exit code to add
 
+    $exit_code = $PS_EXIT_PROG_ERROR unless defined $exit_code;
+
     carp($msg);
     if (defined $det_id and defined $iter and defined $exp_id and not $no_update) {
Index: /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/detrend_stack.pl
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/detrend_stack.pl	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/detrend_stack.pl	(revision 23352)
@@ -18,6 +18,4 @@
 use PS::IPP::Metadata::List qw( parse_md_list );
 use PS::IPP::Config 1.01 qw( :standard );
-
-my $ipprc = PS::IPP::Config->new(); # IPP configuration
 
 use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
@@ -63,16 +61,10 @@
     defined $outroot;
 
-$ipprc->define_camera($camera);
 $det_type = uc($det_type);
 
+my $ipprc = PS::IPP::Config->new( $camera ) or my_die( "Unable to set up", $det_id, $iter, $class_id, $PS_EXIT_CONFIG_ERROR ); # IPP configuration
 my $logDest = $ipprc->filename("LOG.IMFILE", $outroot, $class_id)
     or &my_die("Missing entry in file rules", $det_id, $iter, $class_id, $PS_EXIT_CONFIG_ERROR);
-
-# Unhandled exceptions should be passed on to my_die so they get pushed into the database
-$SIG{__DIE__} = sub { die @_ if $^S;
-                      my_die( $_[0], $det_id, $iter, $class_id, $PS_EXIT_UNKNOWN_ERROR ); };
-
-# optionally redirect the outputs from this script to LOG.IMFILE
-$ipprc->redirect_output($logDest) if $redirect;
+$ipprc->redirect_output($logDest) or my_die( "Unable to redirect output", $det_id, $iter, $class_id, $PS_EXIT_SYS_ERROR ) if $redirect;
 
 # Recipes to use as a function of detrend type
@@ -259,4 +251,6 @@
     my $exit_code = shift; # Exit code to add
 
+    $exit_code = $PS_EXIT_PROG_ERROR unless defined $exit_code;
+
     carp($msg);
     if (defined $det_id and defined $iter and defined $class_id and not $no_update) {
Index: /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/diff_skycell.pl
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/diff_skycell.pl	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/diff_skycell.pl	(revision 23352)
@@ -21,6 +21,4 @@
 use Data::Dumper;
 use PS::IPP::Config 1.01 qw( :standard );
-
-my $ipprc = PS::IPP::Config->new(); # IPP configuration
 
 use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
@@ -62,12 +60,10 @@
     and defined $outroot;
 
-# Unhandled exceptions should be passed on to my_die so they get pushed into the database
-$SIG{__DIE__} = sub { die @_ if $^S;
-                      my_die( $_[0], $diff_id, $skycell_id, $PS_EXIT_UNKNOWN_ERROR ); };
+my $ipprc = PS::IPP::Config->new() or my_die( "Unable to set up", $diff_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) if $redirect;
+$ipprc->redirect_output($logDest) or my_die( "Unable to redirect output", $diff_id, $skycell_id, $PS_EXIT_SYS_ERROR ) if $redirect;
 
 my $source_id = $ipprc->source_id($dbname, $PS_TABLE_ID_DIFF);
@@ -280,4 +276,6 @@
     my $exit_code = shift;      # Exit code to add
 
+    $exit_code = $PS_EXIT_PROG_ERROR unless defined $exit_code;
+
     warn($msg);
     if (defined $diff_id and defined $skycell_id and not $no_update) {
Index: /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/dist_advancerun.pl
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/dist_advancerun.pl	(revision 23352)
+++ /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/dist_advancerun.pl	(revision 23352)
@@ -0,0 +1,154 @@
+#!/usr/bin/env perl
+
+use Carp;
+use warnings;
+use strict;
+
+## report the program and machine
+use Sys::Hostname;
+my $host = hostname();
+print "\n\n";
+print "Starting script $0 on $host\n\n";
+
+use vars qw( $VERSION );
+$VERSION = '0.01';
+
+use IPC::Cmd 0.36 qw( can_run run );
+use File::Temp qw( tempfile );
+use File::Basename qw( basename );
+use PS::IPP::Metadata::Config;
+use PS::IPP::Metadata::List qw( parse_md_list );
+
+use PS::IPP::Config 1.01 qw( :standard );
+
+my $ipprc = PS::IPP::Config->new(); # IPP configuration
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+
+# Parse the command-line arguments
+my ($dist_id, $stage, $stage_id, $clean, $outroot);
+my ($dbname, $save_temps, $verbose, $no_update, $no_op, $logfile);
+
+GetOptions(
+           'dist_id=s'  => \$dist_id,# Magic destreak run identifier
+           'stage=s'        => \$stage,      # raw, chip, warp, or diff
+           'stage_id=s'     => \$stage_id,   # exp_id, chip_id, warp_id, or diff_id
+           'outroot=s'      => \$outroot,    # "directory" for outputs
+           'save-temps'     => \$save_temps, # Save temporary files?
+           'dbname=s'       => \$dbname,     # Database name
+           'verbose'        => \$verbose,    # Print stuff?
+           'no-update'      => \$no_update,  # Don't update the database?
+           'no-op'          => \$no_op,      # Don't do any operations?
+           'logfile=s'      => \$logfile,
+           ) or pod2usage( 2 );
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage( -msg => "Required options: --dist_id --stage --stage_id --outroot",
+           -exitval => 3) unless
+    defined $dist_id and
+    defined $stage and
+    defined $stage_id and
+    defined $outroot;
+
+$ipprc->redirect_output($logfile) if $logfile;
+
+# Look for programs we need
+my $missing_tools;
+my $disttool   = can_run('disttool') or (warn "Can't find disttool" and $missing_tools = 1);
+my $regtool   = can_run('regtool') or (warn "Can't find regtool" and $missing_tools = 1);
+my $chiptool   = can_run('chiptool') or (warn "Can't find chiptool" and $missing_tools = 1);
+my $camtool   = can_run('camtool') or (warn "Can't find camtool" and $missing_tools = 1);
+my $faketool   = can_run('faketool') or (warn "Can't find faketool" and $missing_tools = 1);
+my $warptool   = can_run('warptool') or (warn "Can't find warptool" and $missing_tools = 1);
+my $difftool   = can_run('difftool') or (warn "Can't find difftool" and $missing_tools = 1);
+my $stacktool   = can_run('stacktool') or (warn "Can't find stacktool" and $missing_tools = 1);
+if ($missing_tools) {
+    &my_die("Can't find required tools.", $dist_id, $PS_EXIT_CONFIG_ERROR);
+}
+
+my $mdcParser = PS::IPP::Metadata::Config->new;
+
+my $tool;
+if ($stage eq "raw") {
+    $tool = "regtool";
+} elsif ($stage eq "chip") {
+    $tool = "chiptool";
+} elsif ($stage eq "camera") {
+    $tool = "camtool";
+} elsif ($stage eq "fake") {
+    $tool = "faketool";
+} elsif ($stage eq "warp") {
+    $tool = "warptool";
+} elsif ($stage eq "stack") {
+    $tool = "stacktool";
+} elsif ($stage eq "diff") {
+    $tool = "difftool";
+} else {
+    &my_die("Unexpected stage: $stage", $dist_id, $PS_EXIT_CONFIG_ERROR);
+}
+
+# XXX Should we create a file rule for this
+my $outfile = "$outroot/dbinfo.$stage.$stage_id.mdc";
+
+{
+    my $id_arg = "-$stage" . "_id";
+    my $command = "$tool -exportrun $id_arg $stage_id -outfile $outfile";
+    $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 $command: $error_code", $dist_id, $error_code);
+    }
+}
+
+# set distRun.stage = 'full'
+{
+    my $command = "$disttool -updaterun -dist_id $dist_id -set_state full";
+    $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 $command: $error_code", $dist_id, $error_code);
+    }
+}
+
+
+exit 0;
+
+### Pau.
+
+
+sub my_die
+{
+    my $msg = shift;            # Warning message on die
+    my $dist_id = shift;    # Magic DS identifier
+    my $exit_code = shift;      # Exit code to add
+
+    $exit_code = $PS_EXIT_PROG_ERROR unless defined $exit_code;
+
+    my $command = "$disttool -updaterun";
+    $command   .= " -dist_id $dist_id";
+    $command   .= " -code $exit_code";
+    $command   .= " -dbname $dbname" if defined $dbname;
+
+    # Add the processed file to the database
+    unless ($no_update) {
+        my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+            run(command => $command, verbose => $verbose);
+        unless ($success) {
+            carp("failed to update database for $dist_id");
+        }
+    } else {
+        print "Skipping command: $command\n";
+    }
+
+    carp($msg);
+    exit $exit_code;
+}
+
+__END__
Index: /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/dist_component.pl
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/dist_component.pl	(revision 23352)
+++ /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/dist_component.pl	(revision 23352)
@@ -0,0 +1,348 @@
+#!/usr/bin/env perl
+
+use Carp;
+use warnings;
+use strict;
+
+## report the program and machine
+use Sys::Hostname;
+my $host = hostname();
+print "\n\n";
+print "Starting script $0 on $host\n\n";
+
+use vars qw( $VERSION );
+$VERSION = '0.01';
+
+use IPC::Cmd 0.36 qw( can_run run );
+use File::Temp qw( tempfile );
+use File::Basename qw( basename );
+use Digest::MD5::File qw( file_md5_hex );
+use PS::IPP::Metadata::Config;
+use PS::IPP::Metadata::List qw( parse_md_list );
+
+use PS::IPP::Config 1.01 qw( :standard );
+
+my $ipprc = PS::IPP::Config->new(); # IPP configuration
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+
+# Look for programs we need
+my $missing_tools;
+my $disttool   = can_run('disttool') or (warn "Can't find disttool" and $missing_tools = 1);
+my $streaksrelease   = can_run('streaksrelease') or (warn "Can't find streaksrelease" and $missing_tools = 1);
+if ($missing_tools) {
+    warn("Can't find required tools.");
+    exit($PS_EXIT_CONFIG_ERROR);
+}
+
+# Parse the command-line arguments
+my ($dist_id, $camera, $stage, $stage_id, $component, $path_base, $chip_path_base, $clean);
+my ($outroot, $run_state, $data_state, $magicked);
+my ($dbname, $save_temps, $verbose, $no_update, $no_op, $logfile);
+
+GetOptions(
+           'dist_id=s'  => \$dist_id,# Magic destreak run identifier
+           'camera=s'       => \$camera,     # camera for evaluating file rules
+           'stage=s'        => \$stage,      # raw, chip, warp, or diff
+           'stage_id=s'     => \$stage_id,   # exp_id, chip_id, warp_id, or diff_id
+           'component=s'    => \$component,  # the class_id or skycell_id
+           'path_base=s'    => \$path_base,  # path_base of the input
+           'chip_path_base=s'=> \$chip_path_base,  # path base for camera stage (to enable us to find the mask filefor raw images)
+           'state=s'        => \$run_state,  # state of the run
+           'data_state=s'   => \$data_state, # data_state for this component
+           'magicked=s'     => \$magicked,   # data_state for this component
+           'outroot=s'      => \$outroot,    # "directory" for outputs
+           'clean=s'        => \$clean,      # create clean distribution
+           'save-temps'     => \$save_temps, # Save temporary files?
+           'dbname=s'       => \$dbname,     # Database name
+           'verbose'        => \$verbose,    # Print stuff?
+           'no-update'      => \$no_update,  # Don't update the database?
+           'no-op'          => \$no_op,      # Don't do any operations?
+           'logfile=s'      => \$logfile,
+           ) or pod2usage( 2 );
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage( -msg => "Required options: --dist_id --camera --stage --stage_id --component --path_base --outroot",
+           -exitval => 3) unless
+    defined $dist_id and
+    defined $camera and
+    defined $stage and
+    defined $stage_id and
+    defined $component and
+    defined $path_base and
+    defined $outroot;
+
+$ipprc->redirect_output($logfile) if $logfile;
+
+if (($stage eq 'raw') and !defined $chip_path_base) {
+    pod2usage( -msg => "Required options: --chip_path_base for raw stage", -exitval => 3);
+}
+
+$ipprc->define_camera($camera);
+
+my $mdcParser = PS::IPP::Metadata::Config->new; # Parser for metadata config files
+
+my $basename = basename($path_base);
+
+#
+# are we creating a clean distribution?
+if (defined($clean) and ($clean eq "T")) {
+    $clean = 1;
+} else {
+    $clean = 0;
+}
+
+# making a clean bundle of raw images doesn't make sense
+if (($stage eq "raw") and $clean) {
+    # well I suppose we could ship the log file....
+    &my_die("cannot create clean run at raw stage", $dist_id, $component, $PS_EXIT_CONFIG_ERROR);
+}
+
+
+# create the output directories if it is not a nebulous path and it doesn't exist
+if (index($outroot, "neb://") != 0) {
+    if (! -e $outroot ) {
+        my $code = system "mkdir -p $outroot";
+        &my_die("cannot create output directory $outroot", $dist_id, $component,
+                $code >> 8) if $code;
+    }
+}
+
+my $file_list = get_file_list($clean, $stage, $path_base);
+
+&my_die("failed to compute product list", $dist_id, $component, $PS_EXIT_CONFIG_ERROR) if !$file_list;
+
+&my_die("empty file list", $dist_id, $component, $PS_EXIT_CONFIG_ERROR) if !scalar @$file_list;
+
+
+# set up directory for temporary files
+
+my $tmpdir  = "$outroot/tmpdir.$dist_id.$component.$$";
+if (-e $tmpdir) {
+    if (-d $tmpdir) {
+        my $rc = system "rm -r $tmpdir";
+        &my_die("cannot rm $tmpdir return code: $rc", $dist_id, $component, $PS_EXIT_UNKNOWN_ERROR) if $rc;
+    } else {
+        unlink $tmpdir or &my_die("cannot delete $tmpdir", $dist_id, $component, $PS_EXIT_UNKNOWN_ERROR);
+    }
+}
+mkdir $tmpdir or &my_die("cannot create temporary directory $tmpdir", $dist_id, $component,
+                       $PS_EXIT_UNKNOWN_ERROR);
+
+my @base_list;
+foreach my $file (@$file_list) {
+    my $base = basename($file);
+    push @base_list, $base;
+    my $path = $ipprc->file_resolve($file);
+    symlink $path, "$tmpdir/$base";
+}
+
+if (!$clean) {
+
+    # Here is where we determine whether or not a GPC1 image can be released.
+
+    if ($magicked and ($magicked eq "F")) {
+        $magicked = 0;
+    }
+
+    if ($camera eq "GPC1" and !$magicked) {
+        &my_die("cannot create distribution bundle ${stage}_id $stage_id because the data has not been magic desreaked", $dist_id, $component, $PS_EXIT_DATA_ERROR);
+    }
+
+    # run streaksrelease to set masked pixels to NAN
+    my ($image, $mask, $variance, $class_id);
+    if ($stage eq "raw") {
+        $class_id = $component;
+        $image = $path_base;
+        $mask = $ipprc->filename("PPIMAGE.CHIP.MASK", $chip_path_base, $class_id);
+    } elsif ($stage eq "chip") {
+        $class_id = $component;
+        $image = $ipprc->filename("PPIMAGE.CHIP", $path_base, $class_id);
+        $mask = $ipprc->filename("PPIMAGE.CHIP.MASK", $path_base, $class_id);
+        $variance = $ipprc->filename("PPIMAGE.CHIP.VARIANCE", $path_base, $class_id);
+    } elsif ($stage eq "warp") {
+        $mask   = $ipprc->filename("PSWARP.OUTPUT.MASK", $path_base);
+        $variance = $ipprc->filename("PSWARP.OUTPUT.VARIANCE", $path_base);
+    } elsif ($stage eq "diff") {
+        $mask   = $ipprc->filename("PPSUB.OUTPUT.MASK", $path_base);
+        $variance = $ipprc->filename("PPSUB.OUTPUT.VARIANCE", $path_base);
+    } else {
+        &my_die("not ready for stage: $stage", $dist_id, $component, $PS_EXIT_CONFIG_ERROR);
+    }
+
+    push @base_list, $image;
+    push @base_list, $mask if $mask;
+    push @base_list, $variance if $variance;
+
+    my $command = "$streaksrelease -stage $stage -image $image -outroot $tmpdir";
+    $command .= " -class_id $class_id" if $class_id;
+    $command .= " -mask $mask" if $mask;
+    $command .= " -chip_mask $mask" if ($stage eq 'chip' and $mask);
+    $command .= " -weight $variance" if $variance;
+    unless (defined $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 $command: $error_code", $dist_id, $component, $error_code);
+        }
+    } else {
+        print "skipping command $command\n";
+    }
+}
+
+
+my $file_name;
+my $bytes;
+my $md5sum;
+# create the tarfile
+{
+    # XXX TODO: create a file rule for the tar file name
+    my $tbase = basename($path_base);
+    $tbase .= ".$component" if $component;
+    $file_name = "$tbase.tgz";
+    my $tarfile = "$outroot/$file_name";
+
+    my $command = "tar -C $tmpdir -czhf $tarfile .";
+
+    unless (defined $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 $command: $error_code", $dist_id, $component, $error_code);
+        }
+    } else {
+        print "skipping command $command\n";
+    }
+
+    # tell the module not to die on error
+    $Digest::MD5::File::NOFATALS = 1;
+    $md5sum = file_md5_hex($tarfile);
+    &my_die("unable to compute md5sum for $tarfile", $dist_id, $component, $PS_EXIT_UNKNOWN_ERROR) if !$md5sum;
+
+    my @finfo = stat($tarfile);
+    &my_die("unable to stat $tarfile", $dist_id, $component, $PS_EXIT_UNKNOWN_ERROR) if !@finfo;
+    $bytes = $finfo[7];
+
+    if (!$save_temps) {
+        # delete the temp directory and it's contents
+        system "rm -r $tmpdir";
+    }
+}
+{
+    my $command = "$disttool -addprocessedcomponent -dist_id $dist_id -component $component";
+    $command .= " -name $file_name -bytes $bytes -md5sum $md5sum";
+    $command .= " -dbname $dbname" if defined $dbname;
+
+    unless (defined $no_update) {
+        my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+            run(command => $command, verbose => $verbose);
+        unless ($success) {
+            $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+            &my_die("Unable to perform $command: $error_code", $dist_id, $component, $error_code);
+        }
+    } else {
+        print "skipping command $command\n";
+    }
+}
+
+exit 0;
+
+### Pau.
+
+
+sub get_file_list {
+    my $clean = shift;
+    my $stage = shift;
+    my $path_base = shift;
+
+    my @file_list;
+    if ($stage eq "raw") {
+        push @file_list, $path_base;
+    } else {
+        # TODO: these data will eventually come from the CONFIG dump
+
+        my $fh;
+        open $fh, "/data/ipp004.0/home/bills/ipp/ippScripts/scripts/clean.mdc" or
+            &my_die("cannot find clean.mdc", $dist_id, $component, $PS_EXIT_UNKNOWN_ERROR);
+
+        my @data = <$fh>;
+        my $clean_mdc = join "", @data;
+
+        my $mdlist = $mdcParser->parse($clean_mdc) or
+                &my_die("failed to parse clean.mdc", $dist_id, $component, $PS_EXIT_UNKNOWN_ERROR);
+
+        my $product_lists = parse_md_list($mdlist) or
+                &my_die("failed to parse metadata list", $dist_id, $component, $PS_EXIT_UNKNOWN_ERROR);
+
+
+        my $product_list;
+        foreach my $list (@$product_lists) {
+
+            my $list_name = $list->{STAGE};
+            if (lc ($list_name) eq lc($stage) ) {
+                $product_list = $list;
+            }
+        }
+        &my_die("failed to parse find product list for stage $stage", $dist_id, $component, $PS_EXIT_UNKNOWN_ERROR) if !$product_list;
+
+        # resolve each file rule and add it to the file_list
+        foreach my $rule (keys %$product_list) {
+            next if $rule eq "STAGE";
+            my $keep_on_clean = $product_list->{$rule};
+            my $fn = $ipprc->filename($rule, $path_base, $component) or
+                &my_die("Missing entry from camera config: $rule", $dist_id, $component,
+                    $PS_EXIT_CONFIG_ERROR);
+    #        printf "%-16.16s\t%s\t%s\n",  $rule, $clean, $fn;
+            if (!$clean or $keep_on_clean) {
+                push @file_list, $fn;
+            }
+        }
+    }
+
+    return \@file_list;
+}
+
+
+
+
+sub file_check
+{
+    my $file = shift;           # Name of file
+    &my_die("Unable to find output file: $file", $dist_id, $component, $PS_EXIT_SYS_ERROR) unless
+        $ipprc->file_exists($file);
+}
+
+sub my_die
+{
+    my $msg = shift;            # Warning message on die
+    my $dist_id = shift;    # Magic DS identifier
+    my $component = shift;      # class_id or skycell_id
+    my $exit_code = shift;      # Exit code to add
+
+    $exit_code = $PS_EXIT_PROG_ERROR unless defined $exit_code;
+
+    my $command = "$disttool -addprocessedcomponent";
+    $command   .= " -dist_id $dist_id";
+    $command   .= " -component $component";
+    $command   .= " -code $exit_code";
+    $command   .= " -dbname $dbname" if defined $dbname;
+
+    # Add the processed file to the database
+    unless ($no_update) {
+        my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+            run(command => $command, verbose => $verbose);
+        unless ($success) {
+            carp("failed to update database for $dist_id $component");
+        }
+    } else {
+        print "Skipping command: $command\n";
+    }
+
+    carp($msg);
+    exit $exit_code;
+}
+
+__END__
Index: /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/fake_imfile.pl
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/fake_imfile.pl	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/fake_imfile.pl	(revision 23352)
@@ -36,6 +36,4 @@
 use PS::IPP::Metadata::Config;
 use PS::IPP::Config 1.01 qw( :standard );
-
-my $ipprc = PS::IPP::Config->new(); # IPP configuration
 
 use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
@@ -82,13 +80,7 @@
     defined $outroot;
 
-# Unhandled exceptions should be passed on to my_die so they get pushed into the database
-$SIG{__DIE__} = sub { die @_ if $^S;
-                      my_die( $_[0], $exp_id, $fake_id, $class_id, $PS_EXIT_UNKNOWN_ERROR ); };
-
-$ipprc->define_camera($camera);
-
-my $logDest = $ipprc->filename("LOG.IMFILE", $outroot, $class_id)  or &my_die("Missing entry from camera config", $exp_id, $fake_id, $class_id, $PS_EXIT_CONFIG_ERROR);
-
-$ipprc->redirect_output($logDest) if $redirect;
+my $ipprc = PS::IPP::Config->new( $camera ) or my_die( "Unable to set up", $exp_id, $fake_id, $class_id, $PS_EXIT_CONFIG_ERROR ); # IPP configuration
+my $logDest = $ipprc->filename("LOG.IMFILE", $outroot, $class_id) or &my_die("Missing entry from camera config", $exp_id, $fake_id, $class_id, $PS_EXIT_CONFIG_ERROR);
+$ipprc->redirect_output($logDest) or my_die( "Unable to redirect output", $exp_id, $fake_id, $class_id, $PS_EXIT_SYS_ERROR ) if $redirect;
 
 # Recipes to use based on reduction class
@@ -214,4 +206,6 @@
     my $exit_code = shift; # Exit code to add
 
+    $exit_code = $PS_EXIT_PROG_ERROR unless defined $exit_code;
+
     carp($msg);
     if (defined $exp_id and defined $fake_id and defined $class_id and not $no_update) {
Index: /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/ipp_cleanup.pl
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/ipp_cleanup.pl	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/ipp_cleanup.pl	(revision 23352)
@@ -18,35 +18,45 @@
 use Pod::Usage qw( pod2usage );
 
-my $ipprc = PS::IPP::Config->new(); # this is used for PATH, NEB filename conversions
-
 # Parse the command-line arguments
 my ($stage, $camera, $stage_id, $mode, $path_base, $dbname, $verbose, $no_op, $helplist);
 GetOptions('stage=s'        => \$stage,     # which analysis stage to clean?
-	   'camera|i=s'     => \$camera,    # user-supplied camera name
-	   'stage_id=s'     => \$stage_id,  # id for this stage (only needed for certain stages)
-	   'mode|m=s'       => \$mode,      # cleanup mode (clean / purge)
-	   'path_base=s'    => \$path_base, # basename for files
-	   'dbname|d=s'     => \$dbname,    # Database name
+           'camera|i=s'     => \$camera,    # user-supplied camera name
+           'stage_id=s'     => \$stage_id,  # id for this stage (only needed for certain stages)
+           'mode|m=s'       => \$mode,      # cleanup mode (clean / purge)
+           'path_base=s'    => \$path_base, # basename for files
+           'dbname|d=s'     => \$dbname,    # Database name
            'verbose'        => \$verbose,   # Print to stdout
-	   'no-op'          => \$no_op,     # pretend but don't actually inject
-	   'helplist'       => \$helplist   # give help listing
-	   ) or pod2usage( 2 );
-
-pod2usage( -msg => "remove temporary / all data files for an IPP analysis stage", 
-	   -exitval => 2) if defined $helplist;
-
-pod2usage( -msg => "Usage: $0 --camera (name) --stage (stage) --stage_id (stage_id) --mode (mode) [--path_base (path)] [--dbname dbname] [--no-op] [--help]", 
-	   -exitval => 2 ) if scalar @ARGV;
+           'no-op'          => \$no_op,     # pretend but don't actually inject
+           'helplist'       => \$helplist   # give help listing
+           ) or pod2usage( 2 );
+
+pod2usage( -msg => "remove temporary / all data files for an IPP analysis stage",
+           -exitval => 2) if defined $helplist;
+
+pod2usage( -msg => "Usage: $0 --camera (name) --stage (stage) --stage_id (stage_id) --mode (mode) [--path_base (path)] [--dbname dbname] [--no-op] [--help]",
+           -exitval => 2 ) if scalar @ARGV;
 
 pod2usage( -msg => "Required options:--camera (name) --stage (stage) --mode (mode)",
-	   -exitval => 3) unless 
+           -exitval => 3) unless
     defined $camera and
     defined $stage and
     defined $mode;
 
-# $mode must be one of "goto_cleaned" or "goto_purged"
-unless (($mode eq "goto_cleaned") || ($mode eq "goto_purged")) {
-    die "invalid cleanup mode $mode\n";    
-}
+my $ipprc = PS::IPP::Config->new( $camera ) or my_die("Unable to set up", $stage_id, $PS_EXIT_CONFIG_ERROR); # this is used for PATH, NEB filename conversions
+
+# $mode must be one of "goto_cleaned", "goto_scrubbed", or
+# "goto_purged" goto_cleaned and goto_scrubbed both result in
+# 'cleaned' on success ('scrubbed' allows chips without config files
+# to be cleaned; they cannot be recovered, but the small data is left
+# behind). XXX make 'scrubbed' a data_state?
+unless (($mode eq "goto_cleaned") || ($mode eq "goto_scrubbed") || ($mode eq "goto_purged")) {
+    die "invalid cleanup mode $mode\n";
+}
+
+my $error_state;
+if ($mode eq "goto_cleaned")  { $error_state = "error_cleaned";  }
+if ($mode eq "goto_scrubbed") { $error_state = "error_scrubbed"; }
+if ($mode eq "goto_purged")   { $error_state = "error_purged";   }
+
 
 my %stages = ( chip => 1, camera => 1, fake => 1, warp => 1, stack => 1, diff  => 1);
@@ -55,11 +65,9 @@
 }
 
-$ipprc->define_camera($camera);
-
 my $mdcParser = PS::IPP::Metadata::Config->new; # Parser for metadata config files
 
 # choice of files to delete depends on the stage
 if ($stage eq "chip") {
-    
+
     die "--stage_id required for stage chip\n" if !$stage_id;
     ### select the imfiles for this entry
@@ -78,4 +86,19 @@
         &my_die("Unable to perform chiptool: $error_code", "chip", $stage_id, $error_code);
     }
+
+    # if there are no chipProcessedImfiles (@$stdout_buf == 0), the reset the state to 'new'
+    if (@$stdout_buf == 0)  {
+	my $command = "$chiptool -chip_id $stage_id -updaterun -set_state new";
+	$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 chiptool: $error_code", "chip", $stage_id, $error_code);
+	}
+	exit 0;
+    }
+
     my $metadata = $mdcParser->parse(join "", @$stdout_buf) or
         &my_die("Unable to parse metadata config doc", "chip", $stage_id, $PS_EXIT_PROG_ERROR);
@@ -87,9 +110,10 @@
     # loop over all of the imfiles, determine the path_base and class_id for each
     foreach my $imfile (@$imfiles) {
-	my $class_id = $imfile->{class_id};
-	my $path_base = $imfile->{path_base};
+        my $class_id = $imfile->{class_id};
+        my $path_base = $imfile->{path_base};
         my $status = 1;
 
         # don't clean up unless the data needed to update is available
+        # modes goto_purged and goto_scrubbed will remove files even if the config is non-existent
         if ($mode eq "goto_cleaned") {
             my $config_file = $ipprc->filename("PPIMAGE.CONFIG", $path_base, $class_id);
@@ -124,11 +148,11 @@
                 addFilename (\@files, "PPIMAGE.CONFIG", $path_base, $class_id);
             }
-	
+
             # actual command to delete the files
             $status = &delete_files (\@files);
         }
 
-	if ($status)  {
-	    my $command = "$chiptool -chip_id $stage_id -class_id $class_id";
+        if ($status)  {
+            my $command = "$chiptool -chip_id $stage_id -class_id $class_id";
             if ($mode eq "goto_purged") {
                 $command .= " -topurgedimfile";
@@ -136,26 +160,30 @@
                 $command .= " -tocleanedimfile";
             }
+            $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 chiptool: $error_code", "chip", $stage_id, $error_code);
+            }
+        } else {
+
+	    # if an error happens for one chip, the chipRun will stay in goto_*, but the chips will go to error_* (matching the goto_*)
+	    my $command = "$chiptool -updateprocessedimfile -chip_id $stage_id -class_id $class_id -set_state $error_state";
 	    $command .= " -dbname $dbname" if defined $dbname;
 
-	    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+            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", "chip", $stage_id, $error_code);
-	    }
-        } else {
-	    my $command = "$chiptool -updateprocessedimfile -chip_id $stage_id -class_id $class_id -code 1";
-	    $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 chiptool: $error_code", "chip", $stage_id, $error_code);
-	    }
-	}
-    }
-
-} elsif ($stage eq "camera") {
+            unless ($success) {
+                $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+                &my_die("Unable to perform chiptool: $error_code", "chip", $stage_id, $error_code);
+            }
+        }
+    }
+    exit 0;
+}
+
+if ($stage eq "camera") {
     die "--stage_id required for stage camera\n" if !$stage_id;
     # this stage uses 'camtool'
@@ -210,9 +238,13 @@
 
     if ($status)  {
-        my $command = "$camtool -cam_id $stage_id -updaterun";
+        my $command;
         if ($mode eq "goto_cleaned") {
-            $command .= " -state cleaned";
-        } else {
-            $command .= " -state purged";
+            $command = "$camtool -updaterun -cam_id $stage_id -set_state cleaned";
+	}
+        if ($mode eq "goto_scrubbed") {
+            $command = "$camtool -updaterun -cam_id $stage_id -set_state cleaned";
+	}
+        if ($mode eq "goto_purged") {
+            $command = "$camtool -updaterun -cam_id $stage_id -set_state purged";
         }
         $command .= " -dbname $dbname" if defined $dbname;
@@ -224,5 +256,6 @@
         }
     } else {
-        my $command = "$camtool -updateprocessedexp -cam_id $stage_id -code 1";
+	# since 'camera' has only a single imfile, we can just update the run
+        my $command = "$camtool -updaterun -cam_id $stage_id -set_state $error_state";
         $command .= " -dbname $dbname" if defined $dbname;
 
@@ -236,5 +269,7 @@
     }
     exit 0;
-} elsif ($stage eq "warp") {
+}
+
+if ($stage eq "warp") {
     die "--stage_id required for stage warp\n" if !$stage_id;
     # this stage uses 'warptool'
@@ -295,6 +330,6 @@
         }
 
-	if ($status)  {
-	    my $command = "$warptool -warp_id $stage_id -skycell_id $skycell_id";
+        if ($status)  {
+            my $command = "$warptool -warp_id $stage_id -skycell_id $skycell_id";
             if ($mode eq "goto_purged") {
                 $command .= " -topurgedskyfile";
@@ -302,25 +337,24 @@
                 $command .= " -tocleanedskyfile";
             }
-	    $command .= " -dbname $dbname" if defined $dbname;
-
-	    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+            $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 warptool: $error_code", "warp", $stage_id, $error_code);
-	    }
+            unless ($success) {
+                $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+                &my_die("Unable to perform warptool: $error_code", "warp", $stage_id, $error_code);
+            }
          } else {
-            # XXX: -updateskyfile mode does not exist, need to add it
-	    my $command = "$warptool -updateskyfile -warp_id $stage_id -skycell_id $skycell_id -code 1";
+	    my $command = "$warptool -updateskyfile -warp_id $stage_id -skycell_id $skycell_id -set_state $error_state";
 	    $command .= " -dbname $dbname" if defined $dbname;
 
             my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
                 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", "warp", $stage_id, $error_code);
-	    }
+            unless ($success) {
+                $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+                &my_die("Unable to perform warptool: $error_code", "warp", $stage_id, $error_code);
+            }
             exit $PS_EXIT_UNKNOWN_ERROR;
-	}
+        }
     }
     exit 0;
@@ -329,16 +363,16 @@
 # left TODO
 # fake : faketool : -pendingcleanupimfile (loop over imfiles)
-# stack: stacktool : -pendingcleanupskyfile (loop over skyfiles)
-# diff:  difftool : -pendingcleanupskyfile
+# stack: stacktool : -pendingcleanupskyfile
+# diff:  difftool : -pendingcleanupskyfile (loop over skyfiles)
 
 die "ipp_cleanup.pl -stage $stage not yet implemented\n";
 
-sub delete_files 
+sub delete_files
 {
     my $files = shift; # reference to a list of files to unlink
-    
-    # this script is, of course, very dangerous.  
+
+    # this script is, of course, very dangerous.
     foreach my $file (@$files) {
-	print STDERR "unlinking $file\n";
+        print STDERR "unlinking $file\n";
         $ipprc->file_delete($file);
     }
@@ -346,5 +380,5 @@
 }
 
-sub addFilename 
+sub addFilename
 {
     my $files      = shift; # reference to a list of files to unlink
@@ -359,4 +393,5 @@
 }
 
+# XXX we currently do not set the error state in the db on my_die
 sub my_die
 {
Index: /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/ipp_maskscript.pl
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/ipp_maskscript.pl	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/ipp_maskscript.pl	(revision 23352)
@@ -12,22 +12,19 @@
 use PS::IPP::Config 1.01 qw( :standard );
 
-my $ipprc = PS::IPP::Config->new(); # IPP configuration
-
 my ($dbname, $det_id, $camera);
-
 GetOptions('dbname=s'    => \$dbname,
-	   'det_id=s'    => \$det_id,
-	   'camera|c=s'  => \$camera,
-	   ) or pod2usage( 2 );
+           'det_id=s'    => \$det_id,
+           'camera|c=s'  => \$camera,
+           ) or pod2usage( 2 );
 
 pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
 
 pod2usage(
-	  -msg => "USAGE: ipp_maskscript.pl --dbname (name) --det_id (id) --iter (iteration) --camera (name)",
-	  -exitval => 3,
-	  ) unless defined $dbname and defined $det_id and defined $camera;
+          -msg => "USAGE: ipp_maskscript.pl --dbname (name) --det_id (id) --iter (iteration) --camera (name)",
+          -exitval => 3,
+          ) unless defined $dbname and defined $det_id and defined $camera;
 
 # I could determine the camera from a query for the detrun
-$ipprc->define_camera($camera);
+my $ipprc = PS::IPP::Config->new( $camera ) or my_die("Unable to setup", $PS_EXIT_CONFIG_ERROR); # IPP configuration
 
 ###  Get list of dark imfile results
@@ -47,5 +44,5 @@
 
 # parse the output into a list
-my $mdcParser = PS::IPP::Metadata::Config->new;	# Parser for metadata config files
+my $mdcParser = PS::IPP::Metadata::Config->new; # Parser for metadata config files
 my $metadata = $mdcParser->parse(join "", @$stdout_buf) or
     &my_die("Unable to parse metadata config doc", $PS_EXIT_PROG_ERROR);
@@ -81,5 +78,5 @@
     # print STDERR "contents: @contents\n";
 
-    my $parser = PS::IPP::Metadata::Config->new;	# Parser for metadata config files
+    my $parser = PS::IPP::Metadata::Config->new;        # Parser for metadata config files
     my $statsList = $parser->parse(join "", @contents) or &my_die("Unable to parse metadata for imfile stats", $PS_EXIT_SYS_ERROR);
 
@@ -110,5 +107,5 @@
     open (DATA, ">$component.dat");
     for (my $i = 0; $i < @{$nameX}; $i++) {
-	print DATA "${$nameX}[$i] ${$nameY}[$i]\n";
+        print DATA "${$nameX}[$i] ${$nameY}[$i]\n";
     }
     close (DATA);
@@ -147,10 +144,10 @@
     my ($exp_time, $tag, $md) = @_;
 
-    # descend through the fpa        
+    # descend through the fpa
     foreach my $entry (@$md) {
-	# print STDERR "name: $entry->{name}, class: $entry->{class}\n";
+        # print STDERR "name: $entry->{name}, class: $entry->{class}\n";
         # recurse on nested metadata
         if ($entry->{class} eq 'metadata') {
-	    my $newtag = $tag . "_" . $entry->{name};
+            my $newtag = $tag . "_" . $entry->{name};
             &parse_stats_table ($exp_time, $newtag, $entry->{value});
         }
@@ -161,15 +158,15 @@
                 push @bg_stdev_data, $entry->{value};
             } else {
-		push @bg_name,    $tag;
+                push @bg_name,    $tag;
                 push @bg_data,    $entry->{value};
-		push @bg_exptime, $exp_time;
-		# print STDERR "$tag $exp_time $entry->{value}\n";
+                push @bg_exptime, $exp_time;
+                # print STDERR "$tag $exp_time $entry->{value}\n";
             }
-	    if (!$componentsHash{$tag}) {
-		push @components, $tag;
-		$componentsHash{$tag} = 1;
-	    }
-	    next;
-	} 
+            if (!$componentsHash{$tag}) {
+                push @components, $tag;
+                $componentsHash{$tag} = 1;
+            }
+            next;
+        }
     }
     return 1;
Index: /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/magic_definerun.pl
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/magic_definerun.pl	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/magic_definerun.pl	(revision 23352)
@@ -25,12 +25,19 @@
 use PS::IPP::Config 1.01 qw( :standard );
 
-my $ipprc = PS::IPP::Config->new(); # IPP configuration
-
 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 $magictool = can_run('magictool') or (warn "Can't find magictool" and $missing_tools = 1);
+my $difftool  = can_run('difftool') or (warn "Can't find difftool" and $missing_tools = 1);
+my $warptool  = can_run('warptool') or (warn "Can't find warptool" and $missing_tools = 1);
+if ($missing_tools) {
+    warn("Can't find required tools.");
+    exit($PS_EXIT_CONFIG_ERROR);
+}
+
 # Parse the command-line arguments
 my ($exp_id, $warp_id, $min_diff_id, $label, $workdir, $dbname, $save_temps, $verbose);
-
 GetOptions(
            'exp_id=s'        => \$exp_id,     # exposure identifier
@@ -50,15 +57,5 @@
     defined $warp_id;
 
-# $ipprc->define_camera($camera);
-
-# Look for programs we need
-my $missing_tools;
-my $magictool = can_run('magictool') or (warn "Can't find magictool" and $missing_tools = 1);
-my $difftool  = can_run('difftool') or (warn "Can't find difftool" and $missing_tools = 1);
-my $warptool  = can_run('warptool') or (warn "Can't find warptool" and $missing_tools = 1);
-if ($missing_tools) {
-    warn("Can't find required tools.");
-    exit($PS_EXIT_CONFIG_ERROR);
-}
+my $ipprc = PS::IPP::Config->new() or my_die("Unable to set up", $PS_EXIT_CONFIG_ERROR); # IPP configuration
 
 my $mdcParser = PS::IPP::Metadata::Config->new; # Parser for metadata config files
Index: /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/magic_destreak.pl
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/magic_destreak.pl	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/magic_destreak.pl	(revision 23352)
@@ -21,6 +21,4 @@
 
 use PS::IPP::Config 1.01 qw( :standard );
-
-my $ipprc = PS::IPP::Config->new(); # IPP configuration
 
 use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
@@ -76,8 +74,6 @@
     defined $outroot;
 
-# Unhandled exceptions should be passed on to my_die so they get pushed into the database
-$SIG{__DIE__} = sub { die @_ if $^S;
-                      my_die( $_[0], $magic_ds_id, $component, $PS_EXIT_UNKNOWN_ERROR ); };
-
+my $ipprc = PS::IPP::Config->new( $camera ) or my_die( "Unable to set up", $magic_ds_id, $component, $PS_EXIT_CONFIG_ERROR ); # IPP configuration
+$ipprc->redirect_output($logfile) or my_die( "Unable to redirect output", $magic_ds_id, $component, $PS_EXIT_SYS_ERROR ) if $logfile;
 
 my ($skycell_args, $class_id, $skycell_id);
@@ -94,9 +90,4 @@
     &my_die("Invalid value for stage: $stage", $magic_ds_id, $component, $PS_EXIT_CONFIG_ERROR);
 }
-
-$ipprc->redirect_output($logfile) if $logfile;
-
-$ipprc->define_camera($camera);
-
 
 my $mdcParser = PS::IPP::Metadata::Config->new; # Parser for metadata config files
@@ -276,4 +267,6 @@
     my $exit_code = shift;      # Exit code to add
 
+    $exit_code = $PS_EXIT_PROG_ERROR unless defined $exit_code;
+
     my $command = "$magicdstool -adddestreakedfile";
     $command   .= " -magic_ds_id $magic_ds_id";
Index: /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/magic_mask.pl
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/magic_mask.pl	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/magic_mask.pl	(revision 23352)
@@ -22,6 +22,4 @@
 
 use PS::IPP::Config 1.01 qw( :standard );
-
-my $ipprc = PS::IPP::Config->new(); # IPP configuration
 
 use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
@@ -58,11 +56,6 @@
     defined $outroot;
 
-# Unhandled exceptions should be passed on to my_die so they get pushed into the database
-$SIG{__DIE__} = sub { die @_ if $^S;
-                      my_die( $_[0], $magic_id, $PS_EXIT_UNKNOWN_ERROR ); };
-
-$ipprc->define_camera($camera);
-
-$ipprc->redirect_output($logfile) if $logfile;
+my $ipprc = PS::IPP::Config->new( $camera ) or my_die( "Unable to set up", $magic_id, $PS_EXIT_CONFIG_ERROR ); # IPP configuration
+$ipprc->redirect_output($logfile) or my_die( "Unable to redirect output", $magic_id, $PS_EXIT_SYS_ERROR ) if $logfile;
 
 my $mdcParser = PS::IPP::Metadata::Config->new; # Parser for metadata config files
@@ -153,4 +146,6 @@
     my $exit_code = shift;      # Exit code to add
 
+    $exit_code = $PS_EXIT_PROG_ERROR unless defined $exit_code;
+
     carp($msg);
     if (defined $magic_id and not $no_update) {
Index: /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/magic_process.pl
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/magic_process.pl	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/magic_process.pl	(revision 23352)
@@ -23,6 +23,4 @@
 
 use PS::IPP::Config 1.01 qw( :standard );
-
-my $ipprc = PS::IPP::Config->new(); # IPP configuration
 
 use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
@@ -62,9 +60,6 @@
     defined $outroot;
 
-# Unhandled exceptions should be passed on to my_die so they get pushed into the database
-$SIG{__DIE__} = sub { die @_ if $^S;
-                      my_die( $_[0], $magic_id, $node, $PS_EXIT_UNKNOWN_ERROR ); };
-
-$ipprc->define_camera($camera);
+my $ipprc = PS::IPP::Config->new( $camera ) or my_die( "Unable to set up", $magic_id, $node, $PS_EXIT_CONFIG_ERROR ); # IPP configuration
+$ipprc->redirect_output($logfile) or my_die( "Unable to redirect output", $magic_id, $node, $PS_EXIT_SYS_ERROR ) if $logfile;
 
 # RemoveStreaks doesn't know about nebulous. It expects to be able to append strings to outroot
@@ -73,12 +68,9 @@
 # of the file names as arguments
 if ($outroot =~ 'neb:/') {
-    &my_die("RemoveStreaks does not support nebulous paths in outroot", $magic_id, $node,
-        $PS_EXIT_CONFIG_ERROR);
+    &my_die("RemoveStreaks does not support nebulous paths in outroot", $magic_id, $node, $PS_EXIT_CONFIG_ERROR);
 }
 
 # resolve any path:// or file:// in outroot
 $outroot = $ipprc->file_resolve($outroot);
-
-$ipprc->redirect_output($logfile) if $logfile;
 
 my $mdcParser = PS::IPP::Metadata::Config->new; # Parser for metadata config files
@@ -340,4 +332,6 @@
     my $exit_code = shift;      # Exit code to add
 
+    $exit_code = $PS_EXIT_PROG_ERROR unless defined $exit_code;
+
     carp($msg);
     if (defined $magic_id and defined $node and not $no_update) {
Index: /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/magic_tree.pl
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/magic_tree.pl	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/magic_tree.pl	(revision 23352)
@@ -24,6 +24,4 @@
 use File::Temp qw( tempfile );
 use PS::IPP::Config 1.01 qw( :standard );
-
-my $ipprc = PS::IPP::Config->new(); # IPP configuration
 
 use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
@@ -70,11 +68,6 @@
     defined $outroot;
 
-# Unhandled exceptions should be passed on to my_die so they get pushed into the database
-$SIG{__DIE__} = sub { die @_ if $^S;
-                      my_die( $_[0], $magic_id, $PS_EXIT_UNKNOWN_ERROR ); };
-
-$ipprc->define_camera($camera);
-
-$ipprc->redirect_output($logfile) if $logfile;
+my $ipprc = PS::IPP::Config->new( $camera ) or my_die( "Unable to set up", $magic_id, $PS_EXIT_CONFIG_ERROR ); # IPP configuration
+$ipprc->redirect_output($logfile) or my_die( "Unable to redirect output", $magic_id, $PS_EXIT_SYS_ERROR ) if $logfile;
 
 my $mdcParser = PS::IPP::Metadata::Config->new; # Parser for metadata config files
@@ -297,4 +290,6 @@
     my $exit_code = shift;      # Exit code to add
 
+    $exit_code = $PS_EXIT_PROG_ERROR unless defined $exit_code;
+
     carp($msg);
     if (defined $magic_id and not $no_update) {
Index: /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/register_exp.pl
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/register_exp.pl	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/register_exp.pl	(revision 23352)
@@ -24,6 +24,4 @@
 use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
 use Pod::Usage qw( pod2usage );
-
-my $ipprc = PS::IPP::Config->new();
 
 # Look for commands we need
@@ -53,9 +51,6 @@
 ) or pod2usage( 2 );
 
-# Unhandled exceptions should be passed on to my_die so they get pushed into the database
-$SIG{__DIE__} = sub { die @_ if $^S;
-                      my_die( $_[0], $exp_id, $PS_EXIT_UNKNOWN_ERROR ); };
-
-$ipprc->redirect_output($logfile) if $logfile;
+my $ipprc = PS::IPP::Config->new() or my_die( "Unable to set up", $exp_id, $PS_EXIT_CONFIG_ERROR );
+$ipprc->redirect_output($logfile) or my_die( "Unable to redirect output", $exp_id, $PS_EXIT_SYS_ERROR ) if $logfile;
 
 pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
@@ -192,4 +187,6 @@
     my $exit_code = shift;
 
+    $exit_code = $PS_EXIT_PROG_ERROR unless defined $exit_code;
+
     carp($msg);
     if (defined $exp_id and not $no_update) {
Index: /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/register_imfile.pl
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/register_imfile.pl	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/register_imfile.pl	(revision 23352)
@@ -23,5 +23,4 @@
 use Math::Trig;
 
-my $ipprc = PS::IPP::Config->new(); # IPP configuration
 use File::Spec;
 
@@ -49,9 +48,6 @@
 ) or pod2usage( 2 );
 
-# Unhandled exceptions should be passed on to my_die so they get pushed into the database
-$SIG{__DIE__} = sub { die @_ if $^S;
-                      my_die( $_[0], $exp_id, $tmp_exp_name, $tmp_class_id, $uri, $PS_EXIT_UNKNOWN_ERROR ); };
-
-$ipprc->redirect_output($logfile) if $logfile;
+my $ipprc = PS::IPP::Config->new() or my_die( "Unable to set up", $exp_id, $tmp_exp_name, $tmp_class_id, $uri, $PS_EXIT_CONFIG_ERROR ); # IPP configuration
+$ipprc->redirect_output($logfile) or my_die( "Unable to redirect output", $exp_id, $tmp_exp_name, $tmp_class_id, $uri, $PS_EXIT_SYS_ERROR ) if $logfile;
 
 pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
@@ -98,5 +94,5 @@
     print "STDOUT:\n$out1";
     print "STDERR:\n$err1";
-    &my_die("Unable to perform ppStats on exposure id $exp_id: " . $h1->result(), $exp_id, $tmp_exp_name, $tmp_class_id, $uri, $h1->result() ) unless $result1;
+    &my_die("Unable to perform ppStats on exposure id $exp_id: " . $h1->result(), $exp_id, $tmp_exp_name, $tmp_class_id, $uri, ($h1->result() or $PS_EXIT_PROG_ERROR) ) unless $result1;
 
     print "[Running " . join(' ', @command2) . "]\n";
@@ -107,5 +103,5 @@
     print "STDOUT:\n$out2";
     print "STDERR:\n$err2";
-    &my_die("Unable to perform ppStatsFromMetadata on exposure id $exp_id: " . $h2->result(), $exp_id, $tmp_exp_name, $tmp_class_id, $uri, $h2->result() ) unless $result2;
+    &my_die("Unable to perform ppStatsFromMetadata on exposure id $exp_id: " . $h2->result(), $exp_id, $tmp_exp_name, $tmp_class_id, $uri, ($h2->result() or $PS_EXIT_PROG_ERROR) ) unless $result2;
     chomp $out2;
     $cmdflags = $out2;
@@ -132,10 +128,10 @@
 
 # determine solar-system parameters
-my $longitude = &value_for_flag ($cmdflags, "-longitude");
-my $latitude  = &value_for_flag ($cmdflags, "-latitude");
-my $elevation = &value_for_flag ($cmdflags, "-elevation");
-my $ra        = &value_for_flag ($cmdflags, "-ra");
-my $dec       = &value_for_flag ($cmdflags, "-decl");
-my $dateobs   = &value_for_flag ($cmdflags, "-dateobs");
+my $longitude = &value_for_flag($cmdflags, "-longitude");
+my $latitude  = &value_for_flag($cmdflags, "-latitude");
+my $elevation = &value_for_flag($cmdflags, "-elevation");
+my $ra        = &value_for_flag($cmdflags, "-ra");
+my $dec       = &value_for_flag($cmdflags, "-decl");
+my $dateobs   = &value_for_flag($cmdflags, "-dateobs");
 
 # if the needed data is available, pass it to sunmoon:
@@ -216,4 +212,6 @@
 
     # for failed imfiles, we insert UNKNOWN for inst, telescope, class_id
+
+    $exit_code = $PS_EXIT_PROG_ERROR unless defined $exit_code;
 
     carp($msg);
@@ -230,5 +228,6 @@
         $command .= " -hostname $host" if defined $host;
         $command .= " -dbname $dbname" if defined $dbname;
-        system ($command);
+        print "Running: $command\n";
+        system($command);
     }
     exit $exit_code;
Index: /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/stack_skycell.pl
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/stack_skycell.pl	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/stack_skycell.pl	(revision 23352)
@@ -24,6 +24,4 @@
 use File::Basename;
 use PS::IPP::Config 1.01 qw( :standard );
-
-my $ipprc = PS::IPP::Config->new(); # IPP configuration
 
 use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
@@ -66,7 +64,5 @@
     and defined $run_state;
 
-# Unhandled exceptions should be passed on to my_die so they get pushed into the database
-$SIG{__DIE__} = sub { die @_ if $^S;
-                      my_die( $_[0], $stack_id, $PS_EXIT_UNKNOWN_ERROR ); };
+my $ipprc = PS::IPP::Config->new() or my_die( "Unable to set up", $stack_id, $PS_EXIT_CONFIG_ERROR ); # IPP configuration
 
 # XXX camera is not known here; cannot use filerules...
@@ -81,5 +77,5 @@
 }
 
-$ipprc->redirect_output($logDest) if $redirect;
+$ipprc->redirect_output($logDest) or my_die( "Unable to redirect output", $stack_id, $PS_EXIT_SYS_ERROR ) if $redirect;
 
 my $temp_images_exist = 0;
@@ -300,4 +296,6 @@
     my $exit_code = shift;      # Exit code to add
 
+    $exit_code = $PS_EXIT_PROG_ERROR unless defined $exit_code;
+
     carp($msg);
 
Index: /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/summit_copy.pl
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/summit_copy.pl	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/summit_copy.pl	(revision 23352)
@@ -136,4 +136,6 @@
     my $exit_code = shift; # Exit code to add
 
+    $exit_code = $PS_EXIT_PROG_ERROR unless defined $exit_code;
+
     warn $msg;
     unless ($no_update) {
Index: /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/warp_overlap.pl
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/warp_overlap.pl	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/warp_overlap.pl	(revision 23352)
@@ -23,6 +23,4 @@
 use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
 use Pod::Usage qw( pod2usage );
-
-my $ipprc = PS::IPP::Config->new(); # IPP configuration
 
 # Look for programs we need
@@ -49,6 +47,4 @@
 ) or pod2usage( 2 );
 
-$ipprc->redirect_output($logfile) if $logfile;
-
 pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
 pod2usage(
@@ -59,9 +55,6 @@
     and defined $tess_dir;
 
-# Unhandled exceptions should be passed on to my_die so they get pushed into the database
-$SIG{__DIE__} = sub { die @_ if $^S;
-                      my_die( $_[0], $warp_id, $PS_EXIT_UNKNOWN_ERROR ); };
-
-$ipprc->define_camera($camera);
+my $ipprc = PS::IPP::Config->new( $camera ) or my_die( "Unable to set up", $warp_id, $PS_EXIT_CONFIG_ERROR ); # IPP configuration
+$ipprc->redirect_output($logfile) or my_die( "Unable to redirect output", $warp_id, $PS_EXIT_SYS_ERROR ) if $logfile;
 
 &my_die("Tessellation identifier not provided: $tess_dir", $warp_id, $PS_EXIT_SYS_ERROR) unless $tess_dir ne "NULL";
@@ -193,4 +186,6 @@
     my $warp_id = shift;        # Warp identifier
     my $exit_code = shift;      # Exit code to add
+
+    $exit_code = $PS_EXIT_PROG_ERROR unless defined $exit_code;
 
     carp($msg);
Index: /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/warp_skycell.pl
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/warp_skycell.pl	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/warp_skycell.pl	(revision 23352)
@@ -24,6 +24,4 @@
 use PS::IPP::Metadata::List qw( parse_md_list );
 use PS::IPP::Config 1.01 qw( :standard );
-
-my $ipprc = PS::IPP::Config->new(); # IPP configuration
 
 # Look for programs we need
@@ -68,14 +66,10 @@
     and defined $run_state;
 
-# Unhandled exceptions should be passed on to my_die so they get pushed into the database
-$SIG{__DIE__} = sub { die @_ if $^S;
-                      my_die( $_[0], $warp_id, $skycell_id, $tess_dir, $PS_EXIT_UNKNOWN_ERROR ); };
-
-$ipprc->define_camera($camera);
-
-my $logDest = $ipprc->filename("LOG.EXP", $outroot, $skycell_id);
+my $ipprc = PS::IPP::Config->new( $camera ) or my_die( "Unable to set up", $warp_id, $skycell_id, $tess_dir, $PS_EXIT_CONFIG_ERROR ); # IPP configuration
+
+my $logDest = $ipprc->filename("LOG.EXP", $outroot, $skycell_id) or my_die( "Unable to get log filename", $warp_id, $skycell_id, $tess_dir, $PS_EXIT_SYS_ERROR );
 $logDest .= ".update" if ($run_state eq 'update');
 
-$ipprc->redirect_output($logDest) if $redirect;
+$ipprc->redirect_output($logDest) or my_die( "Unable to redirect output", $warp_id, $skycell_id, $tess_dir, $PS_EXIT_SYS_ERROR ) if $redirect;
 
 my $source_id = $ipprc->source_id($dbname, $PS_TABLE_ID_WARP);
@@ -294,4 +288,6 @@
     my $exit_code = shift;      # Exit code to add
 
+    $exit_code = $PS_EXIT_PROG_ERROR unless defined $exit_code;
+
     warn($msg);
     if (defined $warp_id and defined $skycell_id and defined $tess_dir and not $no_update) {
Index: /branches/cnb_branches/cnb_branch_20090301/ippTasks/Makefile.am
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTasks/Makefile.am	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTasks/Makefile.am	(revision 23352)
@@ -20,4 +20,5 @@
 	summit.copy.pro \
 	replicate.pro \
+	dist.pro \
 	pstamp.pro
 
Index: /branches/cnb_branches/cnb_branch_20090301/ippTasks/automate.pro
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTasks/automate.pro	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTasks/automate.pro	(revision 23352)
@@ -175,4 +175,8 @@
   end
 
+  task.exit    crash
+    showcommand crash
+  end
+
   # operation times out?
   task.exit    timeout
@@ -283,4 +287,8 @@
   end
 
+  task.exit    crash
+    showcommand crash
+  end
+
   # operation times out?
   task.exit    timeout
@@ -373,4 +381,8 @@
   end
 
+  task.exit    crash
+    showcommand crash
+  end
+
   # operation times out?
   task.exit    timeout
@@ -448,4 +460,9 @@
   end
 
+  task.exit    crash
+    book setword automate $pageName pantaskState INIT.REGULAR
+    showcommand crash
+  end
+
   # operation times out?
   task.exit    timeout
Index: /branches/cnb_branches/cnb_branch_20090301/ippTasks/calibration.pro
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTasks/calibration.pro	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTasks/calibration.pro	(revision 23352)
@@ -90,4 +90,8 @@
   end
 
+  task.exit    crash
+    showcommand crash
+  end
+
   # operation times out?
   task.exit    timeout
@@ -166,4 +170,9 @@
   end
 
+  task.exit    crash
+    showcommand crash
+    book setword calBook $options:0 pantaskState CRASH
+  end
+
   # operation timed out?
   task.exit    timeout
Index: /branches/cnb_branches/cnb_branch_20090301/ippTasks/camera.pro
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTasks/camera.pro	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTasks/camera.pro	(revision 23352)
@@ -104,4 +104,8 @@
   end
 
+  task.exit    crash
+    showcommand crash
+  end
+
   # operation times out?
   task.exit    timeout
@@ -177,4 +181,11 @@
   task.exit default
     process_exit camPendingExp $options:0 $JOB_STATUS
+  end
+
+  # locked list
+  task.exit    crash
+    showcommand crash
+    echo "hostname: $JOB_HOSTNAME"
+    book setword camPendingExp $options:0 pantaskState CRASH
   end
 
@@ -236,4 +247,8 @@
   end
 
+  task.exit    crash
+    showcommand crash
+  end
+
   # operation times out?
   task.exit    timeout
@@ -289,4 +304,9 @@
   end
 
+  task.exit    crash
+    showcommand crash
+    book setword camPendingCleanup $options:0 pantaskState CRASH
+  end
+
   # operation timed out?
   task.exit    timeout
Index: /branches/cnb_branches/cnb_branch_20090301/ippTasks/chip.pro
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTasks/chip.pro	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTasks/chip.pro	(revision 23352)
@@ -29,5 +29,5 @@
     active true
   end
-  task chip.promoteexp
+  task chip.advanceexp
     active true
   end
@@ -41,5 +41,5 @@
     active false
   end
-  task chip.promoteexp
+  task chip.advanceexp
     active false
   end
@@ -111,4 +111,8 @@
   task.exit    default
     showcommand failure
+  end
+
+  task.exit    crash
+    showcommand crash
   end
 
@@ -188,4 +192,11 @@
   end
 
+  # locked list
+  task.exit    crash
+    showcommand crash
+    echo "hostname: $JOB_HOSTNAME"
+    book setword chipPendingImfile $options:0 pantaskState CRASH
+  end
+
   # operation timed out?
   task.exit    timeout
@@ -196,9 +207,9 @@
 
 # this variable will cycle through the known database names
-$chip_promote_DB = 0
-
-# promote exposures for which all imfiles have completed processing
+$chip_advance_DB = 0
+
+# advance exposures for which all imfiles have completed processing
 # sets the exposure state to full and queues warp processing if requested
-task	       chip.promoteexp
+task	       chip.advanceexp
   host         local
 
@@ -209,16 +220,16 @@
 
   stdout NULL
-  stderr $LOGDIR/chip.promoteexp.log
-
-  task.exec
-    $run = chiptool -promoteexp -limit 10
+  stderr $LOGDIR/chip.advanceexp.log
+
+  task.exec
+    $run = chiptool -advanceexp -limit 10
     if ($DB:n == 0)
       option DEFAULT
     else
       # save the DB name for the exit tasks
-      option $DB:$chip_promote_DB
-      $run = $run -dbname $DB:$chip_promote_DB
-      $chip_promote_DB ++
-      if ($chip_promote_DB >= $DB:n) set chip_promote_DB = 0
+      option $DB:$chip_advance_DB
+      $run = $run -dbname $DB:$chip_advance_DB
+      $chip_advance_DB ++
+      if ($chip_advance_DB >= $DB:n) set chip_advance_DB = 0
     end
     add_poll_args run
@@ -233,4 +244,8 @@
   task.exit    default
     showcommand failure
+  end
+
+  task.exit    crash
+    showcommand crash
   end
 
@@ -291,4 +306,8 @@
   end
 
+  task.exit    crash
+    showcommand crash
+  end
+
   # operation times out?
   task.exit    timeout
@@ -344,4 +363,9 @@
   end
 
+  task.exit    crash
+    showcommand crash
+    book setword chipPendingCleanup $options:0 pantaskState CRASH
+  end
+
   # operation timed out?
   task.exit    timeout
Index: /branches/cnb_branches/cnb_branch_20090301/ippTasks/detrend.correct.pro
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTasks/detrend.correct.pro	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTasks/detrend.correct.pro	(revision 23352)
@@ -85,7 +85,11 @@
   end
 
+  task.exit    crash
+    showcommand crash
+  end
+
   # operation times out?
   task.exit    timeout
-    showcommand
+    showcommand timeout
   end
 end
@@ -145,4 +149,11 @@
   end
 
+  # locked list
+  task.exit    crash
+    showcommand crash
+    echo "hostname: $JOB_HOSTNAME"
+    book setword detPendingCorrectImfile $options:0 pantaskState CRASH
+  end
+
   # operation times out?
   task.exit    timeout
Index: /branches/cnb_branches/cnb_branch_20090301/ippTasks/detrend.mkruns.pro
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTasks/detrend.mkruns.pro	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTasks/detrend.mkruns.pro	(revision 23352)
@@ -34,4 +34,9 @@
   end
 
+  task.exit crash
+    # send someone email?
+    echo "unable to define a new detrend run"
+  end
+
   # operation timed out?  is the database down?
   task.exit timeout
@@ -60,4 +65,9 @@
   # default exit status
   task.exit default
+    echo "unable to define a new detrend run"
+  end
+
+  task.exit crash
+    # send someone email?
     echo "unable to define a new detrend run"
   end
@@ -92,4 +102,9 @@
   end
 
+  task.exit crash
+    # send someone email?
+    echo "unable to define a new detrend run"
+  end
+
   # operation timed out?  is the database down?
   task.exit timeout
Index: /branches/cnb_branches/cnb_branch_20090301/ippTasks/detrend.norm.pro
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTasks/detrend.norm.pro	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTasks/detrend.norm.pro	(revision 23352)
@@ -168,4 +168,8 @@
   end
 
+  task.exit    crash
+    showcommand crash
+  end
+
   # operation times out?
   task.exit    timeout
@@ -225,4 +229,11 @@
   task.exit    default
     process_exit detPendingNormStatImfile $options:0 $JOB_STATUS
+  end
+
+  # locked list
+  task.exit    crash
+    showcommand crash
+    echo "hostname: $JOB_HOSTNAME"
+    book setword detPendingNormStatImfile $options:0 pantaskState CRASH
   end
 
@@ -278,4 +289,8 @@
   task.exit    default
     showcommand failure
+  end
+
+  task.exit    crash
+    showcommand crash
   end
 
@@ -342,4 +357,11 @@
   end
 
+  # locked list
+  task.exit    crash
+    showcommand crash
+    echo "hostname: $JOB_HOSTNAME"
+    book setword detPendingNormImfile $options:0 pantaskState CRASH
+  end
+
   # operation times out?
   task.exit    timeout
@@ -395,4 +417,8 @@
   end
 
+  task.exit    crash
+    showcommand crash
+  end
+
   # operation times out?
   task.exit    timeout
@@ -452,4 +478,11 @@
   task.exit    default
     process_exit detPendingNormExp $options:0 $JOB_STATUS
+  end
+
+  # locked list
+  task.exit    crash
+    showcommand crash
+    echo "hostname: $JOB_HOSTNAME"
+    book setword detPendingNormExp $options:0 pantaskState CRASH
   end
 
@@ -506,4 +539,8 @@
   end
 
+  task.exit    crash
+    showcommand crash
+  end
+
   # operation times out?
   task.exit    timeout
@@ -558,4 +595,9 @@
   task.exit    default
     process_exit detCleanupNormStatImfile $options:0 $JOB_STATUS
+  end
+
+  task.exit    crash
+    showcommand crash
+    book setword detCleanupNormStatImfile $options:0 pantaskState CRASH
   end
 
@@ -612,4 +654,8 @@
   end
 
+  task.exit    crash
+    showcommand crash
+  end
+
   # operation times out?
   task.exit    timeout
@@ -665,4 +711,9 @@
   task.exit    default
     process_exit detCleanupNormImfile $options:0 $JOB_STATUS
+  end
+
+  task.exit    crash
+    showcommand crash
+    book setword detCleanupNormImfile $options:0 pantaskState CRASH
   end
 
@@ -719,4 +770,8 @@
   end
 
+  task.exit    crash
+    showcommand crash
+  end
+
   # operation times out?
   task.exit    timeout
@@ -773,4 +828,9 @@
   end
 
+  task.exit    crash
+    showcommand crash
+    book setword detCleanupNormExp $options:0 pantaskState CRASH
+  end
+
   # operation timed out?
   task.exit    timeout
Index: /branches/cnb_branches/cnb_branch_20090301/ippTasks/detrend.process.pro
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTasks/detrend.process.pro	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTasks/detrend.process.pro	(revision 23352)
@@ -136,7 +136,11 @@
   end
 
+  task.exit    crash
+    showcommand crash
+  end
+
   # operation times out?
   task.exit    timeout
-    showcommand
+    showcommand timeout
   end
 end
@@ -202,4 +206,11 @@
   end
 
+  # locked list
+  task.exit    crash
+    showcommand crash
+    echo "hostname: $JOB_HOSTNAME"
+    book setword detPendingProcessedImfile $options:0 pantaskState CRASH
+  end
+
   # operation times out?
   task.exit    timeout
@@ -255,4 +266,8 @@
   end
 
+  task.exit    crash
+    showcommand crash
+  end
+
   # operation times out?
   task.exit    timeout
@@ -319,4 +334,11 @@
   end
 
+  # locked list
+  task.exit    crash
+    showcommand crash
+    echo "hostname: $JOB_HOSTNAME"
+    book setword detPendingProcessedExp $options:0 pantaskState CRASH
+  end
+
   # operation times out?
   task.exit    timeout
@@ -371,4 +393,8 @@
   end
 
+  task.exit    crash
+    showcommand crash
+  end
+
   # operation times out?
   task.exit    timeout
@@ -424,4 +450,11 @@
   task.exit    default
     process_exit detCleanupProcessedImfile $options:0 $JOB_STATUS
+  end
+
+  # locked list
+  task.exit    crash
+    showcommand crash
+    echo "hostname: $JOB_HOSTNAME"
+    book setword detCleanupProcessedImfile $options:0 pantaskState CRASH
   end
 
@@ -479,4 +512,8 @@
   end
 
+  task.exit    crash
+    showcommand crash
+  end
+
   # operation times out?
   task.exit    timeout
@@ -533,4 +570,11 @@
   end
 
+  # locked list
+  task.exit    crash
+    showcommand crash
+    echo "hostname: $JOB_HOSTNAME"
+    book setword detCleanupProcessedExp $options:0 pantaskState CRASH
+  end
+
   # operation timed out?
   task.exit    timeout
Index: /branches/cnb_branches/cnb_branch_20090301/ippTasks/detrend.reject.pro
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTasks/detrend.reject.pro	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTasks/detrend.reject.pro	(revision 23352)
@@ -83,7 +83,11 @@
   end
 
+  task.exit    crash
+    showcommand crash
+  end
+
   # operation times out?
   task.exit    timeout
-    showcommand failure
+    showcommand timeout
   end
 end
@@ -143,4 +147,11 @@
   end
 
+  # locked list
+  task.exit    crash
+    showcommand crash
+    echo "hostname: $JOB_HOSTNAME"
+    book setword detRejectExp $options:0 pantaskState CRASH
+  end
+
   # operation times out?
   task.exit    timeout
Index: /branches/cnb_branches/cnb_branch_20090301/ippTasks/detrend.resid.pro
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTasks/detrend.resid.pro	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTasks/detrend.resid.pro	(revision 23352)
@@ -129,4 +129,8 @@
   task.exit    default
     showcommand failure
+  end
+
+  task.exit    crash
+    showcommand crash
   end
 
@@ -203,4 +207,11 @@
   end
 
+  # locked list
+  task.exit    crash
+    showcommand crash
+    echo "hostname: $JOB_HOSTNAME"
+    book setword detPendingResidImfile $options:0 pantaskState CRASH
+  end
+
   # operation times out?
   task.exit    timeout
@@ -254,4 +265,8 @@
   task.exit    default
     showcommand failure
+  end
+
+  task.exit    crash
+    showcommand crash
   end
 
@@ -301,5 +316,5 @@
     stderr $LOGDIR/detrend.resid.exp.log
 
-    $run = detrend_resid_exp.pl --det_id $DET_ID --iteration $ITERATION --exp_id $EXP_ID --exp_tag $EXP_TAG --det_type $DET_TYPE --camera $CAMERA --outroot $outroot --redirect-output --verbose
+    $run = detrend_resid_exp.pl --det_id $DET_ID --iteration $ITERATION --exp_id $EXP_ID --exp_tag $EXP_TAG --det_mode $MODE --det_type $DET_TYPE --camera $CAMERA --outroot $outroot --redirect-output --verbose
 
     add_standard_args run
@@ -318,4 +333,11 @@
   task.exit    default
     process_exit detPendingResidExp $options:0 $JOB_STATUS
+  end
+
+  # locked list
+  task.exit    crash
+    showcommand crash
+    echo "hostname: $JOB_HOSTNAME"
+    book setword detPendingResidExp $options:0 pantaskState CRASH
   end
 
@@ -372,4 +394,8 @@
   end
 
+  task.exit    crash
+    showcommand crash
+  end
+
   # operation times out?
   task.exit    timeout
@@ -426,4 +452,11 @@
   task.exit    default
     process_exit detCleanupResidImfile $options:0 $JOB_STATUS
+  end
+
+  # locked list
+  task.exit    crash
+    showcommand crash
+    echo "hostname: $JOB_HOSTNAME"
+    book setword detCleanupResidImfile $options:0 pantaskState CRASH
   end
 
@@ -481,4 +514,8 @@
   end
 
+  task.exit    crash
+    showcommand crash
+  end
+
   # operation times out?
   task.exit    timeout
@@ -536,4 +573,11 @@
   end
 
+  # locked list
+  task.exit    crash
+    showcommand crash
+    echo "hostname: $JOB_HOSTNAME"
+    book setword detCleanupResidExp $options:0 pantaskState CRASH
+  end
+
   # operation timed out?
   task.exit    timeout
Index: /branches/cnb_branches/cnb_branch_20090301/ippTasks/detrend.stack.pro
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTasks/detrend.stack.pro	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTasks/detrend.stack.pro	(revision 23352)
@@ -101,4 +101,8 @@
   end
 
+  task.exit    crash
+    showcommand crash
+  end
+
   # operation times out?
   task.exit    timeout
@@ -163,4 +167,11 @@
   task.exit    default
     process_exit detPendingStackedImfile $options:0 $JOB_STATUS
+  end
+
+  # locked list
+  task.exit    crash
+    showcommand crash
+    echo "hostname: $JOB_HOSTNAME"
+    book setword detPendingStackedImfile $options:0 pantaskState CRASH
   end
 
@@ -217,4 +228,8 @@
   end
 
+  task.exit    crash
+    showcommand crash
+  end
+
   # operation times out?
   task.exit    timeout
@@ -272,4 +287,11 @@
   end
 
+  # locked list
+  task.exit    crash
+    showcommand crash
+    echo "hostname: $JOB_HOSTNAME"
+    book setword detCleanupStackedImfile $options:0 pantaskState CRASH
+  end
+
   # operation timed out?
   task.exit    timeout
Index: /branches/cnb_branches/cnb_branch_20090301/ippTasks/diff.pro
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTasks/diff.pro	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTasks/diff.pro	(revision 23352)
@@ -112,4 +112,8 @@
   end
 
+  task.exit    crash
+    showcommand crash
+  end
+
   # operation times out?
   task.exit    timeout
@@ -173,4 +177,11 @@
   task.exit    default
     process_exit diffSkyfile $options:0 $JOB_STATUS
+  end
+
+  # locked list
+  task.exit    crash
+    showcommand crash
+    echo "hostname: $JOB_HOSTNAME"
+    book setword diffSkyfile $options:0 pantaskState CRASH
   end
 
@@ -229,4 +240,8 @@
   end
 
+  task.exit    crash
+    showcommand crash
+  end
+
   # operation times out?
   task.exit    timeout
@@ -282,4 +297,9 @@
   end
 
+  task.exit    crash
+    showcommand crash
+    book setword diffCleanup $options:0 pantaskState CRASH
+  end
+
   # operation timed out?
   task.exit    timeout
Index: /branches/cnb_branches/cnb_branch_20090301/ippTasks/dist.pro
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTasks/dist.pro	(revision 23352)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTasks/dist.pro	(revision 23352)
@@ -0,0 +1,267 @@
+## dist.pro : support for the production of distribution bundles : -*- sh -*-
+
+# test for required global variables
+check.globals
+
+#$LOGSUBDIR = $LOGDIR/dist
+#exec mkdir -p $LOGSUBDIR
+
+### Initialise the books containing the tasks to do
+book init distToProcess
+book init distToAdvance
+
+### Database lists
+$distToProcess_DB = 0
+$distToAdvance_DB = 0
+
+### Check status of tasks
+macro dist.status
+  book listbook distToProcess
+  book listbook distToAdvance
+end
+
+### Reset tasks
+macro dist.reset
+  book init distToProcess
+  book init distToAdvance
+end
+
+### Turn tasks on
+macro dist.on
+  task dist.process.load
+    active true
+  end
+  task dist.process.run
+    active true
+  end
+  task dist.advance.load
+    active true
+  end
+  task dist.advance.run
+    active true
+  end
+end
+macro dist.off
+  task dist.process.load
+    active false
+  end
+  task dist.process.run
+    active false
+  end
+  task dist.advance.load
+    active false
+  end
+  task dist.advance.run
+    active false
+  end
+end
+
+task	       dist.process.load
+  host         local
+
+  periods      -poll $LOADPOLL
+  periods      -exec $LOADEXEC
+  periods      -timeout 30
+  npending     1
+
+  stdout NULL
+  stderr $LOGDIR/dist.process.log
+
+  task.exec
+    $run = disttool -pendingcomponent
+    if ($DB:n == 0)
+      option DEFAULT
+    else
+      # save the DB name for the exit tasks
+      option $DB:$distToProcess_DB
+      $run = $run -dbname $DB:$distToProcess_DB
+      $distToProcess_DB ++
+      if ($distToProcess_DB >= $DB:n) set distToProcess_DB = 0
+    end
+    add_poll_args run
+    command $run
+  end
+
+  # success
+  task.exit    0
+    # convert 'stdout' to book format
+    ipptool2book stdout distToProcess -key dist_id:component -uniq -setword dbname $options:0 -setword pantaskState INIT
+    if ($VERBOSE > 2)
+      book listbook distToProcess
+    end
+
+    # delete existing entries in the appropriate pantaskStates
+    process_cleanup distToProcess
+  end
+
+  # locked list
+  task.exit    default
+    showcommand failure
+  end
+
+  # operation times out?
+  task.exit    timeout
+    showcommand timeout
+  end
+end
+
+task	       dist.process.run
+  periods      -poll $RUNPOLL
+  periods      -exec $RUNEXEC
+  periods      -timeout 60
+
+  task.exec
+    book npages distToProcess -var N
+    if ($N == 0) break
+    if ($NETWORK == 0) break
+    
+    # look for new components to process (pantaskState == INIT)
+    book getpage distToProcess 0 -var pageName -key pantaskState INIT
+    if ("$pageName" == "NULL") break
+
+    book setword distToProcess $pageName pantaskState RUN
+    book getword distToProcess $pageName dist_id -var DIST_ID
+    book getword distToProcess $pageName camera -var CAMERA
+    book getword distToProcess $pageName stage -var STAGE
+    book getword distToProcess $pageName stage_id -var STAGE_ID
+    book getword distToProcess $pageName clean -var CLEAN
+    book getword distToProcess $pageName component -var COMPONENT
+    book getword distToProcess $pageName path_base -var PATH_BASE
+    book getword distToProcess $pageName chip_path_base -var CHIP_PATH_BASE
+    book getword distToProcess $pageName state -var STATE
+    book getword distToProcess $pageName data_state -var DATA_STATE
+    book getword distToProcess $pageName magicked -var MAGICKED
+    book getword distToProcess $pageName outroot -var OUTROOT
+    book getword distToProcess $pageName dbname -var DBNAME
+
+#    set.host.for.camera $CAMERA $MAGIC_ID
+#    set.workdir.by.camera $CAMERA $MAGIC_ID $WORKDIR_TEMPLATE $default_host WORKDIR
+    host anyhost
+
+    sprintf logfile "%s/dist.%s.%s.log" $OUTROOT $DIST_ID $COMPONENT
+
+    $run = dist_component.pl --dist_id $DIST_ID --camera $CAMERA --stage $STAGE --stage_id $STAGE_ID --component $COMPONENT --path_base $PATH_BASE --chip_path_base $CHIP_PATH_BASE --state $STATE --data_state $DATA_STATE --magicked $MAGICKED --clean $CLEAN --outroot $OUTROOT --logfile $logfile
+
+    add_standard_args run
+
+    # save the pageName for future reference below
+    options $pageName
+
+    # create the command line
+    if ($VERBOSE > 1)
+      echo command $run
+    end
+    command $run
+  end
+
+  # default exit status
+  task.exit    default
+    process_exit distToProcess $options:0 $JOB_STATUS
+  end
+
+  # operation timed out?
+  task.exit    timeout
+    showcommand timeout
+    book setword distToProcess $options:0 pantaskState TIMEOUT
+  end
+end
+
+
+task	       dist.advance.load
+  host         local
+
+  periods      -poll $LOADPOLL
+  periods      -exec $LOADEXEC
+  periods      -timeout 30
+  npending     1
+
+  stdout NULL
+  stderr $LOGDIR/dist.advance.load.log
+
+  task.exec
+    $run = disttool -toadvance
+    if ($DB:n == 0)
+      option DEFAULT
+    else
+      # save the DB name for the exit tasks
+      option $DB:$distToAdvance_DB
+      $run = $run -dbname $DB:$distToAdvance_DB
+      $distToAdvance_DB ++
+      if ($distToAdvance_DB >= $DB:n) set distToAdvance_DB = 0
+    end
+    add_poll_args run
+    command $run
+  end
+
+  # success
+  task.exit    0
+    # convert 'stdout' to book format
+    ipptool2book stdout distToAdvance -key dist_id -uniq -setword dbname $options:0 -setword pantaskState INIT
+    if ($VERBOSE > 2)
+      book listbook distToAdvance
+    end
+
+    # delete existing entries in the appropriate pantaskStates
+    process_cleanup distToAdvance
+  end
+
+  # locked list
+  task.exit    default
+    showcommand failure
+  end
+
+  # operation times out?
+  task.exit    timeout
+    showcommand timeout
+  end
+end
+
+task	       dist.advance.run
+  periods      -poll $RUNPOLL
+  periods      -exec $RUNEXEC
+  periods      -timeout 60
+
+  task.exec
+    book npages distToAdvance -var N
+    if ($N == 0) break
+    if ($NETWORK == 0) break
+    
+    # look for new components to process (pantaskState == INIT)
+    book getpage distToAdvance 0 -var pageName -key pantaskState INIT
+    if ("$pageName" == "NULL") break
+
+    book setword distToAdvance $pageName pantaskState RUN
+    book getword distToAdvance $pageName dist_id -var DIST_ID
+    book getword distToAdvance $pageName stage   -var STAGE
+    book getword distToAdvance $pageName stage_id -var STAGE_ID
+    book getword distToAdvance $pageName outroot -var OUTROOT
+
+    host anyhost
+
+    sprintf logfile "%s/dist.advance.%s.log" $OUTROOT $DIST_ID
+
+    $run = dist_advancerun.pl --dist_id $DIST_ID --stage $STAGE --stage_id $STAGE_ID --outroot $OUTROOT --logfile $logfile
+    add_standard_args run
+
+    # save the pageName for future reference below
+    options $pageName
+
+    # create the command line
+    if ($VERBOSE > 1)
+      echo command $run
+    end
+    command $run
+  end
+
+  # default exit status
+  task.exit    default
+    process_exit distToAdvance $options:0 $JOB_STATUS
+  end
+
+  # operation timed out?
+  task.exit    timeout
+    showcommand timeout
+    book setword distToAdvance $options:0 pantaskState TIMEOUT
+  end
+end
+
Index: /branches/cnb_branches/cnb_branch_20090301/ippTasks/fake.pro
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTasks/fake.pro	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTasks/fake.pro	(revision 23352)
@@ -26,5 +26,5 @@
     active true
   end
-  task fake.promoteexp
+  task fake.advanceexp
     active true
   end
@@ -38,5 +38,5 @@
     active false
   end
-  task fake.promoteexp
+  task fake.advanceexp
     active false
   end
@@ -176,4 +176,11 @@
   end
 
+  # locked list
+  task.exit    crash
+    showcommand crash
+    echo "hostname: $JOB_HOSTNAME"
+    book setword fakePendingImfile $options:0 pantaskState CRASH
+  end
+
   # operation timed out?
   task.exit    timeout
@@ -183,8 +190,8 @@
 end
 
-# promote exposures for which all imfiles have completed processing
+# advance exposures for which all imfiles have completed processing
 # sets the exposure state to full and queues warp processing if requested
-$fake_promote_DB = 0
-task	       fake.promoteexp
+$fake_advance_DB = 0
+task	       fake.advanceexp
   host         local
 
@@ -195,16 +202,16 @@
 
   stdout NULL
-  stderr $LOGDIR/fake.promoteexp.log
-
-  task.exec
-    $run = faketool -promoteexp -limit 10
+  stderr $LOGDIR/fake.advanceexp.log
+
+  task.exec
+    $run = faketool -advanceexp -limit 10
     if ($DB:n == 0)
       option DEFAULT
     else
       # save the DB name for the exit tasks
-      option $DB:$fake_promote_DB
-      $run = $run -dbname $DB:$fake_promote_DB
-      $fake_promote_DB ++
-      if ($fake_promote_DB >= $DB:n) set fake_promote_DB = 0
+      option $DB:$fake_advance_DB
+      $run = $run -dbname $DB:$fake_advance_DB
+      $fake_advance_DB ++
+      if ($fake_advance_DB >= $DB:n) set fake_advance_DB = 0
     end
     add_poll_args run
@@ -219,4 +226,8 @@
   task.exit    default
     showcommand failure
+  end
+
+  task.exit    crash
+    showcommand crash
   end
 
@@ -278,4 +289,8 @@
   end
 
+  task.exit    crash
+    showcommand crash
+  end
+
   # operation times out?
   task.exit    timeout
@@ -331,4 +346,9 @@
   end
 
+  task.exit    crash
+    showcommand crash
+    book setword fakePendingCleanup $options:0 pantaskState CRASH
+  end
+
   # operation timed out?
   task.exit    timeout
Index: /branches/cnb_branches/cnb_branch_20090301/ippTasks/flatcorr.pro
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTasks/flatcorr.pro	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTasks/flatcorr.pro	(revision 23352)
@@ -104,4 +104,8 @@
   end
 
+  task.exit    crash
+    showcommand crash
+  end
+
   # operation times out?
   task.exit    timeout
@@ -157,4 +161,8 @@
   end
 
+  task.exit    crash
+    showcommand crash
+  end
+
   # operation times out?
   task.exit    timeout
@@ -229,4 +237,9 @@
   end
 
+  task.exit    crash
+    showcommand crash
+    book setword flatcorrBook $options:0 pantaskState CRASH
+  end
+
   # operation timed out?
   task.exit    timeout
Index: /branches/cnb_branches/cnb_branch_20090301/ippTasks/magic.pro
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTasks/magic.pro	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTasks/magic.pro	(revision 23352)
@@ -118,4 +118,8 @@
   end
 
+  task.exit    crash
+    showcommand crash
+  end
+
   # operation times out?
   task.exit    timeout
@@ -189,4 +193,9 @@
   end
 
+  task.exit    crash
+    showcommand crash
+    book setword magicToTree $options:0 pantaskState CRASH
+  end
+
   # operation timed out?
   task.exit    timeout
@@ -239,4 +248,8 @@
   end
 
+  task.exit    crash
+    showcommand crash
+  end
+
   # operation times out?
   task.exit    timeout
@@ -302,4 +315,9 @@
   task.exit    default
     process_exit magicToProcess $options:0 $JOB_STATUS
+  end
+
+  task.exit    crash
+    showcommand crash
+    book setword magicToProcess $options:0 pantaskState CRASH
   end
 
@@ -353,4 +371,8 @@
   task.exit    default
     showcommand failure
+  end
+
+  task.exit    crash
+    showcommand crash
   end
 
@@ -423,4 +445,9 @@
   end
 
+  task.exit    crash
+    showcommand crash
+    book setword magicToDS $options:0 pantaskState CRASH
+  end
+
   # operation timed out?
   task.exit    timeout
Index: /branches/cnb_branches/cnb_branch_20090301/ippTasks/pstamp.pro
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTasks/pstamp.pro	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTasks/pstamp.pro	(revision 23352)
@@ -97,4 +97,8 @@
     end
 
+    task.exit   crash
+        showcommand crash
+    end
+
     task.exit   timeout
         showcommand timeout
@@ -136,4 +140,8 @@
     end
 
+    task.exit   crash
+        showcommand crash
+    end
+
     task.exit   timeout
         showcommand timeout
@@ -178,4 +186,9 @@
         showcommand failure
         process_exit pstampRequest $options:0 $JOB_STATUS
+    end
+
+    task.exit crash
+        showcommand crash
+        book setword pstampRequest $options:0 pantaskState CRASH
     end
 
@@ -219,4 +232,8 @@
     end
 
+    task.exit   crash
+        showcommand crash
+    end
+
     task.exit   timeout
         showcommand timeout
@@ -263,4 +280,9 @@
         process_exit pstampFinish $options:0 $JOB_STATUS
         showcommand failure
+    end
+
+    task.exit crash
+        showcommand crash
+        book setword pstampFinish $options:0 pantaskState CRASH.run
     end
 
@@ -307,4 +329,8 @@
     end
 
+    task.exit   crash
+        showcommand crash
+    end
+
     task.exit   timeout
         showcommand timeout
@@ -362,4 +388,11 @@
     end
 
+    task.exit crash
+        echo pstamp.job.run task.crash $JOB_ID status: $JOB_STATUS
+        process_exit pstampJob $options:0 $JOB_STATUS
+        showcommand crash
+        book setword pstampJob $options:0 pantaskState CRASH
+    end
+
     task.exit timeout
         echo pstamp.job.run task.timeout $JOB_ID status: $JOB_STATUS
Index: /branches/cnb_branches/cnb_branch_20090301/ippTasks/register.pro
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTasks/register.pro	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTasks/register.pro	(revision 23352)
@@ -180,4 +180,8 @@
   end
 
+  task.exit    crash
+    showcommand crash
+  end
+
   # operation times out?
   task.exit    timeout
@@ -255,4 +259,11 @@
   task.exit default
     process_exit regPendingImfile $options:0 $JOB_STATUS
+  end
+
+  # locked list
+  task.exit    crash
+    showcommand crash
+    echo "hostname: $JOB_HOSTNAME"
+    book setword regPendingImfile $options:0 pantaskState CRASH
   end
 
@@ -306,4 +317,8 @@
   task.exit    default
     showcommand failure
+  end
+
+  task.exit    crash
+    showcommand crash
   end
 
@@ -392,4 +407,16 @@
   end
 
+  # locked list
+  task.exit    crash
+    showcommand crash
+    echo "hostname: $JOB_HOSTNAME"
+    book setword regPendingExp $options:0 pantaskState CRASH
+  end
+
+  task.exit    crash
+    showcommand crash
+    book setword regPendingExp $options:0 pantaskState CRASH
+  end
+
   # operation times out?
   task.exit    timeout
Index: /branches/cnb_branches/cnb_branch_20090301/ippTasks/replicate.pro
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTasks/replicate.pro	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTasks/replicate.pro	(revision 23352)
@@ -62,4 +62,11 @@
 # the replicate process interacts with only the single Nebulous server
 
+# Each 'pendingreplicate' query is limited to a finite number of so_id values.  
+# Each time we call replicate.load, we increment SO_ID_START by the range value.  
+# If the pendingreplicate query exits with exit status 10, we start over at 0
+
+$SO_ID_START = 0
+$SO_ID_RANGE = 100000
+
 # select Nebulous objects which desire additional copies
 task	       replicate.load
@@ -68,5 +75,5 @@
   # modify these after the tasks are tested
   periods      -poll 10
-  periods      -exec 1
+  periods      -exec 10
   periods      -timeout 1500
   npending     1
@@ -79,5 +86,6 @@
       # command does not need to be dynamic, but having it so allows us to adjust the periods
       # so that we dont have to wait 10 minutes for things to start up
-      command neb-admin --host $NEB_HOST --db $NEB_DB --user $NEB_USER --pass $NEB_PASS --pendingreplicate --limit 7500
+      # XXX smaller limited?  7500 will be a huge book of things to do...
+      command neb-admin --host $NEB_HOST --db $NEB_DB --user $NEB_USER --pass $NEB_PASS --pendingreplicate --limit 7500 --so_id_start $SO_ID_START --so_id_range $SO_ID_RANGE
       periods      -exec 1800
 
@@ -86,4 +94,7 @@
   # success
   task.exit $EXIT_SUCCESS
+    # advance the so_id counter
+    $SO_ID_START += $SO_ID_RANGE
+
     # convert 'stdout' to book format
     ipptool2book stdout replicatePending -key key -uniq -setword pantaskState INIT
@@ -97,7 +108,17 @@
   end
 
+  # out of so_id range, reset
+  task.exit 10
+    # advance the so_id counter
+    $SO_ID_START = 0
+  end
+
   # locked list
   task.exit    default
     showcommand failure
+  end
+
+  task.exit    crash
+    showcommand crash
   end
 
@@ -158,4 +179,9 @@
   end
 
+  task.exit    crash
+    showcommand crash
+    book setword replicatePending $options:0 pantaskState CRASH
+  end
+
   # operation timed out?
   task.exit    timeout
Index: /branches/cnb_branches/cnb_branch_20090301/ippTasks/stack.pro
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTasks/stack.pro	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTasks/stack.pro	(revision 23352)
@@ -112,4 +112,8 @@
   end
 
+  task.exit    crash
+    showcommand crash
+  end
+
   # operation times out?
   task.exit    timeout
@@ -172,4 +176,11 @@
   task.exit    default
     process_exit stackSumSkyfile $options:0 $JOB_STATUS
+  end
+
+  # locked list
+  task.exit    crash
+    showcommand crash
+    echo "hostname: $JOB_HOSTNAME"
+    book setword stackSumSkyfile $options:0 pantaskState CRASH
   end
 
@@ -229,4 +240,8 @@
   end
 
+  task.exit    crash
+    showcommand crash
+  end
+
   # operation times out?
   task.exit    timeout
@@ -282,4 +297,9 @@
   end
 
+  task.exit    crash
+    showcommand crash
+    book setword stackCleanup $options:0 pantaskState CRASH
+  end
+
   # operation timed out?
   task.exit    timeout
Index: /branches/cnb_branches/cnb_branch_20090301/ippTasks/summit.copy.pro
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTasks/summit.copy.pro	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTasks/summit.copy.pro	(revision 23352)
@@ -126,4 +126,7 @@
         showcommand failure
     end
+    task.exit crash
+        showcommand crash
+    end
     task.exit timeout
         showcommand timeout
@@ -182,4 +185,7 @@
     showcommand failure
   end
+  task.exit     crash
+    showcommand crash
+  end
   task.exit     timeout
     showcommand timeout
@@ -228,4 +234,7 @@
           queueprint stderr
         end
+    end
+    task.exit crash
+        showcommand crash
     end
     task.exit timeout
@@ -296,4 +305,9 @@
         showcommand failure
         process_exit pzPendingExp $options:0 $JOB_STATUS
+    end
+
+    task.exit crash
+        showcommand crash
+        book setword pzPendingExp $options:0 pantaskState CRASH
     end
 
@@ -344,4 +358,7 @@
         showcommand failure
     end
+    task.exit     crash
+        showcommand crash
+    end
     task.exit     timeout
         showcommand timeout
@@ -413,4 +430,7 @@
         book setword pzPendingImfile $pageName filename $FILENAME
 
+	stdout $LOGDIR/summit.copy.log
+	stderr $LOGDIR/summit.copy.log
+
         $run = summit_copy.pl --uri $URI --filename $FILENAME --exp_name $EXP_NAME --inst $CAMERA --telescope $TELESCOPE --class $CLASS --class_id $CLASS_ID --bytes $BYTES --md5 $MD5SUM --end_stage reg --workdir $workdir --dbname $DBNAME --timeout 120 --verbose --copies 2
 	if ($COMPRESS) 
@@ -442,4 +462,9 @@
     end
 
+    task.exit crash
+        showcommand crash
+        book setword pzPendingImfile $options:0 pantaskState CRASH
+    end 
+
     # operation timed out?
     task.exit timeout
@@ -476,4 +501,7 @@
     task.exit default
         showcommand failure
+    end
+    task.exit crash
+        showcommand crash
     end
     task.exit timeout
@@ -518,4 +546,8 @@
   end
 
+  task.exit    crash
+    showcommand crash
+  end
+
   # operation times out?
   task.exit    timeout
Index: /branches/cnb_branches/cnb_branch_20090301/ippTasks/warp.pro
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTasks/warp.pro	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTasks/warp.pro	(revision 23352)
@@ -127,4 +127,8 @@
   end
 
+  task.exit    crash
+    showcommand crash
+  end
+
   # operation times out?
   task.exit    timeout
@@ -185,4 +189,11 @@
   task.exit    default
     process_exit warpInputExp $options:0 $JOB_STATUS
+  end
+
+  # locked list
+  task.exit    crash
+    showcommand crash
+    echo "hostname: $JOB_HOSTNAME"
+    book setword warpInputExp $options:0 pantaskState CRASH
   end
 
@@ -239,4 +250,8 @@
   task.exit    default
     showcommand failure
+  end
+
+  task.exit    crash
+    showcommand crash
   end
 
@@ -303,4 +318,11 @@
   end
 
+  # locked list
+  task.exit    crash
+    showcommand crash
+    echo "hostname: $JOB_HOSTNAME"
+    book setword warpPendingSkyCell $options:0 pantaskState CRASH
+  end
+
   # operation timed out?
   task.exit    timeout
@@ -357,4 +379,8 @@
   end
 
+  task.exit    crash
+    showcommand crash
+  end
+
   # operation times out?
   task.exit    timeout
@@ -410,4 +436,9 @@
   end
 
+  task.exit    crash
+    showcommand crash
+    book setword warpPendingCleanup $options:0 pantaskState CRASH
+  end
+
   # operation timed out?
   task.exit    timeout
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/scripts/test_create_detrend.sql
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/scripts/test_create_detrend.sql	(revision 23352)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/scripts/test_create_detrend.sql	(revision 23352)
@@ -0,0 +1,135 @@
+-- disable constraints
+
+DROP TABLE IF EXISTS detRun;
+DROP TABLE IF EXISTS detProcessedImfile;
+DROP TABLE IF EXISTS detNormalizedStatImfile;
+DROP TABLE IF EXISTS detResidImfile;
+
+CREATE TABLE detRun (
+    det_id BIGINT AUTO_INCREMENT,
+    iteration INT,
+    det_type VARCHAR(64),
+    mode VARCHAR(64),
+    state VARCHAR(64),
+    filelevel VARCHAR(64),
+    workdir VARCHAR(255),
+    camera VARCHAR(64),
+    telescope VARCHAR(64),
+    exp_type VARCHAR(64),
+    reduction VARCHAR(64),
+    filter VARCHAR(64),
+    airmass_min FLOAT,
+    airmass_max FLOAT,
+    exp_time_min FLOAT,
+    exp_time_max FLOAT,
+    ccd_temp_min FLOAT,
+    ccd_temp_max FLOAT,
+    posang_min DOUBLE,
+    posang_max DOUBLE,
+    registered DATETIME,
+    time_begin DATETIME,
+    time_end DATETIME,
+    use_begin DATETIME,
+    use_end DATETIME,
+    solang_min FLOAT,
+    solang_max FLOAT,
+    label VARCHAR(64),
+    ref_det_id BIGINT,
+    ref_iter INT,
+    -- parent INT, :: dropping this
+    PRIMARY KEY(det_id),
+    KEY(det_id),
+    KEY(iteration),
+    KEY(det_type),
+    KEY(mode),
+    KEY(state),
+    KEY(label),
+    -- KEY(parent), :: dropping this
+    INDEX(det_id, iteration))
+ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE detProcessedImfile (
+    det_id BIGINT,
+    exp_id BIGINT,
+    class_id VARCHAR(64),
+    uri VARCHAR(255),
+    recipe VARCHAR(64),
+    bg DOUBLE,
+    bg_stdev DOUBLE,
+    bg_mean_stdev DOUBLE,
+    fringe_0 DOUBLE,
+    fringe_1 DOUBLE,
+    fringe_2 DOUBLE,
+    user_1 DOUBLE,
+    user_2 DOUBLE,
+    user_3 DOUBLE,
+    user_4 DOUBLE,
+    user_5 DOUBLE,
+    path_base VARCHAR(255),
+    data_state VARCHAR(64),
+    fault SMALLINT NOT NULL,
+    PRIMARY KEY(det_id, exp_id, class_id),
+    KEY(fault),
+    INDEX(det_id, class_id),
+    INDEX(det_id, exp_id)
+    -- FOREIGN KEY (det_id, exp_id)
+    --     REFERENCES  detInputExp(det_id, exp_id),
+    -- FOREIGN KEY (exp_id, class_id)
+    --     REFERENCES  rawImfile(exp_id, class_id)
+) ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE detNormalizedStatImfile (
+    det_id BIGINT,
+    iteration INT,
+    class_id VARCHAR(64),
+    norm FLOAT,
+    data_state VARCHAR(64),
+    fault SMALLINT NOT NULL,
+    PRIMARY KEY(det_id, iteration, class_id),
+    KEY(fault)
+    -- FOREIGN KEY (det_id, iteration)
+    -- REFERENCES  detInputExp(det_id, iteration),
+    -- FOREIGN KEY (det_id, iteration, class_id)
+    -- REFERENCES  detStackedImfile(det_id, iteration, class_id)
+) ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE detResidImfile (
+    det_id BIGINT,
+    iteration INT,
+    ref_det_id BIGINT,
+    ref_iter INT,
+    exp_id BIGINT,
+    class_id VARCHAR(64),
+    uri VARCHAR(255),
+    recipe VARCHAR(64),
+    bg DOUBLE,
+    bg_stdev DOUBLE,
+    bg_mean_stdev DOUBLE,
+    bg_skewness DOUBLE,
+    bg_kurtosis DOUBLE,
+    bin_stdev DOUBLE,
+    fringe_0 DOUBLE,
+    fringe_1 DOUBLE,
+    fringe_2 DOUBLE,
+    fringe_resid_0 DOUBLE,
+    fringe_resid_1 DOUBLE,
+    fringe_resid_2 DOUBLE,
+    user_1 DOUBLE,
+    user_2 DOUBLE,
+    user_3 DOUBLE,
+    user_4 DOUBLE,
+    user_5 DOUBLE,
+    path_base VARCHAR(255),
+    data_state VARCHAR(64),
+    fault SMALLINT NOT NULL,
+    PRIMARY KEY(det_id, iteration, exp_id, class_id),
+    KEY(fault),
+    INDEX(det_id, iteration, exp_id)
+    -- FOREIGN KEY (det_id, iteration, exp_id)
+    -- REFERENCES  detInputExp(det_id, iteration, exp_id),
+    -- FOREIGN KEY (det_id, exp_id, class_id)
+    -- REFERENCES  detProcessedImfile(det_id, exp_id, class_id),
+    -- FOREIGN KEY (ref_det_id, ref_iter)
+    -- REFERENCES  detNormalizedExp(det_id, iteration)
+) ENGINE=innodb DEFAULT CHARSET=latin1;
+
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/scripts/test_insert_detrend.sql
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/scripts/test_insert_detrend.sql	(revision 23352)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/scripts/test_insert_detrend.sql	(revision 23352)
@@ -0,0 +1,47 @@
+-- create a fake detrun, add fake detProcessedImfile entries, etc
+
+insert into 
+       detRun (det_id, det_type, iteration, camera, workdir,   state, mode) 
+       values (1,      'flat',           0, 'GPC1', 'testdir', 'run', 'master');
+
+insert into 
+       detProcessedImfile (det_id, class_id) 
+       values (1, 'ccd00');
+
+insert into 
+       detProcessedImfile (det_id, class_id) 
+       values (1, 'ccd01');
+
+insert into 
+       detResidImfile (det_id, iteration, class_id) 
+       values (1, 0, 'ccd00');
+
+insert into 
+       detResidImfile (det_id, iteration, class_id) 
+       values (1, 0, 'ccd01');
+
+insert into 
+       detNormalizedStatImfile (det_id, iteration, class_id) 
+       values (1, 0, 'ccd01');
+
+insert into 
+       detNormalizedStatImfile (det_id, iteration, class_id) 
+       values (1, 0, 'ccd00');
+
+update detRun set iteration = 1 where det_id = 1;
+
+insert into 
+       detResidImfile (det_id, iteration, class_id) 
+       values (1, 1, 'ccd00');
+
+insert into 
+       detResidImfile (det_id, iteration, class_id) 
+       values (1, 1, 'ccd01');
+
+insert into 
+       detNormalizedStatImfile (det_id, iteration, class_id) 
+       values (1, 1, 'ccd00');
+
+insert into 
+       detNormalizedStatImfile (det_id, iteration, class_id) 
+       values (1, 1, 'ccd01');
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/share/Makefile.am
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/share/Makefile.am	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/share/Makefile.am	(revision 23352)
@@ -84,4 +84,9 @@
      difftool_skyfile.sql \
      difftool_todiffskyfile.sql \
+     disttool_pendingcomponent.sql \
+     disttool_processedcomponent.sql \
+     disttool_revertrun_update.sql \
+     disttool_revertrun_delete.sql \
+     disttool_toadvance.sql \
      faketool_change_exp_state.sql \
      faketool_change_imfile_data_state.sql \
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/share/chiptool_change_imfile_data_state.sql
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/share/chiptool_change_imfile_data_state.sql	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/share/chiptool_change_imfile_data_state.sql	(revision 23352)
@@ -7,8 +7,2 @@
     chip_id = %lld
     AND class_id = '%s'
-    -- only update if chipRun.state has the expected value
-    AND (
-        SELECT state from chipRun where chipRun.chip_id = chipProcessedImfile.chip_id
-    ) = '%s'
-    
-
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/share/chiptool_pendingcleanupimfile.sql
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/share/chiptool_pendingcleanupimfile.sql	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/share/chiptool_pendingcleanupimfile.sql	(revision 23352)
@@ -15,3 +15,5 @@
     ((chipRun.state = 'goto_cleaned' AND chipProcessedImfile.data_state = 'full')
 OR 
+    (chipRun.state = 'goto_scrubbed' AND chipProcessedImfile.data_state = 'full')
+OR 
     (chipRun.state = 'goto_purged' AND chipProcessedImfile.data_state != 'purged'))
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/share/chiptool_pendingcleanuprun.sql
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/share/chiptool_pendingcleanuprun.sql	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/share/chiptool_pendingcleanuprun.sql	(revision 23352)
@@ -7,3 +7,3 @@
 USING (exp_id)
 WHERE
-    (chipRun.state = 'goto_cleaned' OR chipRun.state = 'goto_purged')
+    (chipRun.state = 'goto_cleaned' OR chipRun.state = 'goto_scrubbed' OR chipRun.state = 'goto_purged')
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/share/dettool_processedimfile.sql
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/share/dettool_processedimfile.sql	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/share/dettool_processedimfile.sql	(revision 23352)
@@ -1,2 +1,3 @@
+-- is this DISTINCT needed?
 SELECT DISTINCT
   detRun.det_type,
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/share/dettool_residimfile.sql
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/share/dettool_residimfile.sql	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/share/dettool_residimfile.sql	(revision 23352)
@@ -2,9 +2,13 @@
    detRun.det_type,
    detRun.mode,
-   detResidImfile.*,
-   rawExp.exp_time
- FROM detResidImfile
- JOIN detRun
-   USING(det_id, iteration)
- JOIN rawExp
-   USING(exp_id)
+   rawExp.exp_time,
+   detResidImfile.*
+FROM detResidImfile
+JOIN detRun
+  USING(det_id, iteration)
+JOIN detInputExp
+  ON detRun.det_id = detInputExp.det_id
+  AND detRun.iteration = detInputExp.iteration
+  AND detResidImfile.exp_id = detInputExp.exp_id
+JOIN rawExp
+  ON rawExp.exp_id = detResidImfile.exp_id
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/share/dettool_todetrunsummary.sql
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/share/dettool_todetrunsummary.sql	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/share/dettool_todetrunsummary.sql	(revision 23352)
@@ -40,4 +40,5 @@
         AND detRunSummary.det_id IS NULL
         AND detRunSummary.iteration IS NULL
+	AND detResidExp.fault = 0
     GROUP BY
         detRun.det_id,
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/share/dettool_tonormalizedstat.sql
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/share/dettool_tonormalizedstat.sql	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/share/dettool_tonormalizedstat.sql	(revision 23352)
@@ -1,58 +1,34 @@
--- select detRun.det_id (det_id)
--- select detRun.iteration
--- by:
--- find the current iteration bassed on det_id
--- find all exp_ids in the current det_id/iteration from detInputExp
--- sort to detInputExp.imfiles to find the largest value per det_id/iter
--- compare imfiles to the number of detStackedImfiles by class_id
--- and:
--- ???
--- det_id is not in detStackedImfile;
--- iteration is not in detStackedImfile;
-
-SELECT
-    det_id,
-    det_type,
-    iteration,
-    camera,
-    workdir,
-    class_id
-FROM
-    (SELECT DISTINCT
-        detRun.det_id,
-        detRun.det_type,
-        detRun.iteration,
-        detRun.workdir,
-        rawExp.camera,
-        detStackedImfile.class_id,
-        rawImfile.class_id as rawimfile_class_id
-    FROM detRun
-    JOIN detInputExp
-        ON detRun.det_id = detInputExp.det_id
-        AND detRun.iteration = detInputExp.iteration
-    JOIN rawExp
-        ON detInputExp.exp_id = rawExp.exp_id
-    JOIN rawImfile
-        ON rawExp.exp_id = rawImfile.exp_id
-    LEFT JOIN detStackedImfile
-        ON detInputExp.det_id = detStackedImfile.det_id
-        AND detInputExp.iteration = detStackedImfile.iteration
-        AND rawImfile.class_id = detStackedImfile.class_id
-    LEFT JOIN detNormalizedStatImfile
-        ON detStackedImfile.det_id = detNormalizedStatImfile.det_id
-        AND detStackedImfile.iteration = detNormalizedStatImfile.iteration
-        AND detStackedImfile.class_id = detNormalizedStatImfile.class_id
-    WHERE
-        detRun.state = 'run'
-        AND detRun.mode = 'master'
-        AND detNormalizedStatImfile.det_id IS NULL
-        AND detNormalizedStatImfile.iteration IS NULL
-        AND detNormalizedStatImfile.class_id IS NULL
-    GROUP BY
---        rawExp.exp_id,
-        detRun.iteration,
-        detRun.det_id
-    HAVING
-        COUNT(rawImfile.class_id) = COUNT(detStackedImfile.class_id)
-        AND SUM(detStackedImfile.fault) = 0
-    ) as tonormalizedstat
+-- a det_run + iteration is ready for normstat when: all detResidImfile entries for that iteration corresponding to all detProcessedImfile entries are available 
+SELECT 
+  detRun.det_id, 
+  detRun.det_type, 
+  detRun.iteration, 
+  detRun.camera, 
+  detRun.workdir, 
+  detProcessedImfile.class_id,
+  COUNT(detProcessedImfile.class_id),
+  COUNT(detResidImfile.class_id)
+FROM detRun
+JOIN detProcessedImfile
+    ON  detProcessedImfile.det_id    = detRun.det_id
+LEFT JOIN detResidImfile
+    ON  detResidImfile.det_id    = detProcessedImfile.det_id
+    AND detResidImfile.exp_id    = detProcessedImfile.exp_id
+    AND detResidImfile.class_id  = detProcessedImfile.class_id
+    AND detResidImfile.iteration = detRun.iteration
+LEFT JOIN detNormalizedStatImfile
+    ON  detNormalizedStatImfile.det_id    = detRun.det_id    
+    AND detNormalizedStatImfile.iteration = detRun.iteration 
+    AND detNormalizedStatImfile.class_id  = detProcessedImfile.class_id
+WHERE
+    detRun.state = 'run'
+    AND detRun.mode = 'master'
+    AND detNormalizedStatImfile.det_id IS NULL
+    AND detNormalizedStatImfile.iteration IS NULL
+    AND detNormalizedStatImfile.class_id IS NULL
+GROUP BY
+    detRun.iteration,
+    detRun.det_id
+HAVING
+    COUNT(detProcessedImfile.class_id) = COUNT(detResidImfile.class_id)
+    AND SUM(detResidImfile.fault) = 0
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/share/dettool_toresidexp.sql
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/share/dettool_toresidexp.sql	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/share/dettool_toresidexp.sql	(revision 23352)
@@ -5,4 +5,6 @@
 -- id, detrend type, and whether the exposure was included in the stack for
 -- this iteration.
+
+-- require the corresponding detNormalizedExp to complete before starting
 
 -- select detRun.det_id
@@ -18,5 +20,5 @@
 -- detResidImfile.{det_id, iteration, exp_id} is not in detResidExp
 
-SELECT DISTINCT
+SELECT
     det_id,
     iteration,
@@ -28,16 +30,57 @@
     workdir,
     exp_tag
-FROM
-    (SELECT DISTINCT
+FROM 
+(   SELECT
         detRun.det_id AS det_id,
         detRun.iteration,
         detRun.det_type,
         detRun.mode,
-        detRun.workdir,
         detInputExp.exp_id,
         detInputExp.include,
         rawExp.camera,
-        rawExp.exp_tag,
-        detResidImfile.class_id
+        detRun.workdir,
+        rawExp.exp_tag
+    FROM detRun
+    JOIN detNormalizedExp
+    	USING(det_id, iteration)
+    JOIN detInputExp
+        USING(det_id, iteration)
+    JOIN rawExp
+        ON detInputExp.exp_id = rawExp.exp_id
+    JOIN rawImfile
+        on rawExp.exp_id = rawImfile.exp_id
+    LEFT JOIN detResidImfile
+        ON detRun.det_id = detResidImfile.det_id
+        AND detRun.iteration = detResidImfile.iteration
+        AND detInputExp.exp_id = detResidImfile.exp_id
+        AND rawImfile.class_id = detResidImfile.class_id
+    LEFT JOIN detResidExp
+        ON detResidImfile.det_id = detResidExp.det_id
+        AND detResidImfile.iteration = detResidExp.iteration
+        AND detResidImfile.exp_id = detResidExp.exp_id
+    WHERE
+        detRun.state = 'run'
+        AND detRun.mode = 'master'
+        AND detResidExp.det_id IS NULL
+        AND detResidExp.iteration IS NULL
+        AND detResidExp.exp_id IS NULL
+    GROUP BY
+        detInputExp.exp_id,
+        detRun.iteration,
+        detRun.det_id
+    HAVING
+        COUNT(rawImfile.class_id) = COUNT(detResidImfile.class_id)
+        AND SUM(detResidImfile.fault) = 0
+    UNION
+    SELECT
+        detRun.det_id AS det_id,
+        detRun.iteration,
+        detRun.det_type,
+        detRun.mode,
+        detInputExp.exp_id,
+        detInputExp.include,
+        rawExp.camera,
+        detRun.workdir,
+        rawExp.exp_tag
     FROM detRun
     JOIN detInputExp
@@ -58,4 +101,5 @@
     WHERE
         detRun.state = 'run'
+        AND detRun.mode = 'verify'
         AND detResidExp.det_id IS NULL
         AND detResidExp.iteration IS NULL
@@ -68,3 +112,3 @@
         COUNT(rawImfile.class_id) = COUNT(detResidImfile.class_id)
         AND SUM(detResidImfile.fault) = 0
-    ) AS toresidexp
+) as temp
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/share/dettool_toresidimfile.sql
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/share/dettool_toresidimfile.sql	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/share/dettool_toresidimfile.sql	(revision 23352)
@@ -13,5 +13,6 @@
     detProcessedImfile.class_id,
     detProcessedImfile.uri,
-    detNormalizedImfile.uri AS det_uri,
+--  detNormalizedImfile.uri AS det_uri,
+    detStackedImfile.uri AS det_uri,
     detRun.det_id AS ref_det_id,
     detRun.iteration AS ref_iter,
@@ -26,11 +27,17 @@
     ON detRun.det_id = detProcessedImfile.det_id
     AND detInputExp.exp_id = detProcessedImfile.exp_id
-JOIN detNormalizedImfile
-    ON detRun.det_id = detNormalizedImfile.det_id
-    AND detRun.iteration = detNormalizedImfile.iteration
-    AND detProcessedImfile.class_id = detNormalizedImfile.class_id
-JOIN detNormalizedExp
-    ON detRun.det_id = detNormalizedExp.det_id
-    AND detRun.iteration = detNormalizedExp.iteration
+JOIN detStackedImfile
+    ON detRun.det_id = detStackedImfile.det_id
+    AND detRun.iteration = detStackedImfile.iteration
+    AND detProcessedImfile.class_id = detStackedImfile.class_id
+-- EAM : replacing detNormalizedImfile with detStackedImfile to change the sequencing
+-- JOIN detNormalizedImfile
+--     ON detRun.det_id = detNormalizedImfile.det_id
+--     AND detRun.iteration = detNormalizedImfile.iteration
+--     AND detProcessedImfile.class_id = detNormalizedImfile.class_id
+-- EAM : we there is no reason to wait for all stacks to complete before continuing
+-- JOIN detNormalizedExp
+--     ON detRun.det_id = detNormalizedExp.det_id
+--     AND detRun.iteration = detNormalizedExp.iteration
 LEFT JOIN detResidImfile
     ON detRun.det_id = detResidImfile.det_id
@@ -41,6 +48,7 @@
     detRun.state = 'run'
     AND detRun.mode = 'master'
-    AND detNormalizedImfile.fault = 0
-    AND detNormalizedExp.fault = 0
+    AND detStackedImfile.fault = 0
+--  AND detNormalizedImfile.fault = 0
+--  AND detNormalizedExp.fault = 0
     AND detResidImfile.det_id IS NULL
     AND detResidImfile.iteration IS NULL
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/share/disttool_pendingcomponent.sql
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/share/disttool_pendingcomponent.sql	(revision 23352)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/share/disttool_pendingcomponent.sql	(revision 23352)
@@ -0,0 +1,154 @@
+SELECT * FROM (
+SELECT
+    distRun.dist_id,
+    stage,
+    stage_id,
+    rawImfile.class_id AS component,
+    clean,
+    rawExp.camera,
+    outroot,
+    rawImfile.uri as path_base,
+    chipProcessedImfile.path_base as chip_path_base,
+    NULL as state,
+    NULL as data_state,
+    rawImfile.magicked
+FROM distRun
+JOIN rawExp ON exp_id = stage_id
+JOIN rawImfile using(exp_id)
+JOIN chipProcessedImfile
+    ON distRun.chip_id = chipProcessedImfile.chip_id
+    AND rawImfile.class_id = chipProcessedImfile.class_id
+LEFT JOIN distComponent 
+    ON distRun.dist_id = distComponent.dist_id 
+    AND rawImfile.class_id = distComponent.component
+WHERE
+    distRun.state = 'new'
+    AND distRun.stage = 'raw'
+    AND distComponent.dist_id IS NULL
+
+-- chip stage
+UNION
+SELECT
+    distRun.dist_id,
+    stage,
+    stage_id,
+    chipProcessedImfile.class_id AS component,
+    clean,
+    rawExp.camera,
+    outroot,
+    chipProcessedImfile.path_base,
+    chipProcessedImfile.path_base as chip_path_base,
+    chipRun.state,
+    chipProcessedImfile.data_state,
+    chipProcessedImfile.magicked
+FROM distRun
+JOIN chipRun ON chipRun.chip_id = distRun.stage_id
+JOIN rawExp using(exp_id)
+JOIN chipProcessedImfile ON chipProcessedImfile.chip_id = stage_id
+LEFT JOIN distComponent 
+    ON distRun.dist_id = distComponent.dist_id 
+    AND chipProcessedImfile.class_id = distComponent.component
+WHERE
+    distRun.state = 'new'
+    AND distRun.stage = 'chip'
+    AND distComponent.dist_id IS NULL
+UNION
+SELECT
+    distRun.dist_id,
+    stage,
+    stage_id,
+    warpSkyfile.skycell_id AS component,
+    clean,
+    rawExp.camera,
+    outroot,
+    warpSkyfile.path_base,
+    NULL as chip_path_base,
+    warpRun.state,
+    warpSkyfile.data_state,
+    warpSkyfile.magicked
+FROM distRun
+JOIN warpRun ON stage_id = warp_id
+JOIN warpSkyfile using(warp_id)
+JOIN fakeRun USING(fake_id)
+JOIN camRun USING(cam_id)
+JOIN chipRun ON camRun.chip_id = chipRun.chip_id
+JOIN rawExp using(exp_id)
+LEFT JOIN distComponent 
+    ON distRun.dist_id = distComponent.dist_id 
+    AND warpSkyfile.skycell_id = distComponent.component
+WHERE
+    distRun.state = 'new'
+    AND distRun.stage = 'warp'
+    AND distComponent.dist_id IS NULL
+UNION
+SELECT
+    distRun.dist_id,
+    stage,
+    stage_id,
+    diffSkyfile.skycell_id AS component,
+    clean,
+    rawExp.camera,
+    outroot,
+    diffSkyfile.path_base,
+    NULL as chip_path_base,
+    diffRun.state,
+    -- data_state doesn't exist yet
+    -- diffSkyfile.data_state,
+    'full' AS data_state,
+    diffSkyfile.magicked
+FROM distRun
+JOIN diffRun ON stage_id = diff_id
+JOIN diffSkyfile using(diff_id)
+JOIN rawExp using(exp_id)
+LEFT JOIN distComponent 
+    ON distRun.dist_id = distComponent.dist_id 
+    AND diffSkyfile.skycell_id = distComponent.component
+WHERE
+    distRun.state = 'new'
+    AND distRun.stage = 'diff'
+    AND distComponent.dist_id IS NULL
+UNION
+SELECT DISTINCT
+    distRun.dist_id,
+    stage,
+    stage_id,
+    stackRun.skycell_id AS component,
+    clean,
+    rawExp.camera,
+    outroot,
+    stackSumSkyfile.path_base,
+    NULL as chip_path_base,
+    stackRun.state,
+    -- data_state doesn't exist yet
+    -- stackSumSkyfile.data_state,
+    'full' AS data_state,
+    0 AS magicked
+FROM distRun
+JOIN stackRun
+    ON stage_id = stack_id
+JOIN stackSumSkyfile
+    USING(stack_id)
+JOIN stackInputSkyfile
+    USING(stack_id)
+JOIN warpSkyfile
+    ON  stackInputSkyfile.warp_id = warpSkyfile.warp_id
+    AND stackRun.skycell_id       = warpSkyfile.skycell_id
+    AND stackRun.tess_id          = warpSkyfile.tess_id
+JOIN warpRun
+    ON warpRun.warp_id = warpSkyfile.warp_id
+JOIN fakeRun
+    USING(fake_id)
+JOIN camRun
+    USING(cam_id)
+JOIN chipRun
+    ON camRun.chip_id = chipRun.chip_id
+JOIN rawExp 
+     USING (exp_id)
+LEFT JOIN distComponent 
+    ON distRun.dist_id = distComponent.dist_id 
+    AND stackRun.skycell_id = distComponent.component
+WHERE
+    distRun.state = 'new'
+    AND distRun.stage = 'stack'
+    AND distComponent.dist_id IS NULL
+) as Foo
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/share/disttool_processedcomponent.sql
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/share/disttool_processedcomponent.sql	(revision 23352)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/share/disttool_processedcomponent.sql	(revision 23352)
@@ -0,0 +1,2 @@
+SELECT *
+FROM    distComponent
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/share/disttool_revertrun_delete.sql
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/share/disttool_revertrun_delete.sql	(revision 23352)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/share/disttool_revertrun_delete.sql	(revision 23352)
@@ -0,0 +1,4 @@
+DELETE FROM distComponent
+USING distComponent, distRun
+WHERE distComponent.dist_id = distRun.dist_id
+    AND distComponent.fault != 0
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/share/disttool_revertrun_update.sql
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/share/disttool_revertrun_update.sql	(revision 23352)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/share/disttool_revertrun_update.sql	(revision 23352)
@@ -0,0 +1,5 @@
+UPDATE distRun
+JOIN distComponent USING(dist_id)
+SET distRun.state = 'new', distRun.fault = 0
+WHERE distComponent.fault != 0
+    OR distRun.fault != 0
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/share/disttool_toadvance.sql
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/share/disttool_toadvance.sql	(revision 23352)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/share/disttool_toadvance.sql	(revision 23352)
@@ -0,0 +1,116 @@
+SELECT DISTINCT
+    dist_id,
+    stage,
+    stage_id,
+    outroot
+FROM
+    (
+-- raw stage
+SELECT 
+    distRun.dist_id,
+    stage,
+    stage_id,
+    outroot
+    FROM distRun
+    JOIN rawImfile ON stage_id = rawImfile.exp_id
+    LEFT JOIN distComponent
+        ON distRun.dist_id = distComponent.dist_id
+        AND distComponent.component = rawImfile.class_id
+    WHERE
+        distRun.state = 'new'
+        AND distRun.fault = 0
+        AND distRun.stage = 'raw'
+    GROUP BY
+        distRun.dist_id,
+        rawImfile.exp_id
+    HAVING
+        COUNT(rawImfile.class_id) = COUNT(distComponent.component)
+        AND SUM(distComponent.fault) = 0
+UNION
+-- chip stage
+SELECT 
+    distRun.dist_id,
+    stage,
+    stage_id,
+    outroot
+    FROM distRun
+    JOIN chipProcessedImfile ON stage_id = chipProcessedImfile.chip_id
+    LEFT JOIN distComponent
+        ON distComponent.dist_id = distRun.dist_id
+        AND distComponent.component = chipProcessedImfile.class_id
+    WHERE
+        distRun.state = 'new'
+        AND distRun.fault = 0
+        AND distRun.stage = 'chip'
+    GROUP BY
+        dist_id,
+        chipProcessedImfile.chip_id
+    HAVING
+        COUNT(chipProcessedImfile.class_id) = COUNT(distComponent.component)
+        AND SUM(distComponent.fault) = 0
+UNION
+-- warp stage
+SELECT 
+    distRun.dist_id,
+    stage,
+    stage_id,
+    outroot
+    FROM distRun
+    JOIN warpSkyfile on stage_id = warp_id
+    LEFT JOIN distComponent
+        ON distRun.dist_id = distComponent.dist_id
+        AND distComponent.component = warpSkyfile.skycell_id
+    WHERE
+        distRun.state = 'new'
+        AND distRun.fault = 0
+        AND distRun.stage = 'warp'
+--        AND warpSkyfile.fault = 0
+--        AND warpSkyfile.ignored = 0
+    GROUP BY
+        distRun.dist_id,
+        warp_id
+    HAVING
+        COUNT(warpSkyfile.skycell_id) = COUNT(distComponent.component)
+        AND SUM(distComponent.fault) = 0
+UNION
+-- diff stage
+SELECT DISTINCT
+    distRun.dist_id,
+    stage,
+    stage_id,
+    outroot
+    FROM distRun
+    JOIN diffSkyfile
+        ON distRun.dist_id = diffSkyfile.diff_id
+    LEFT JOIN distComponent
+        ON distRun.dist_id = distComponent.dist_id
+        AND distComponent.component = diffSkyfile.skycell_id
+    WHERE
+        distRun.state = 'new'
+        AND distRun.fault = 0
+        AND distRun.stage = 'diff'
+--        AND diffSkyfile.fault = 0
+    GROUP BY
+        distRun.dist_id,
+        diff_id
+    HAVING
+        COUNT(diffSkyfile.skycell_id) = COUNT(distComponent.component)
+        AND SUM(distComponent.fault) = 0
+UNION
+-- stack stage
+SELECT 
+    distRun.dist_id,
+    stage,
+    stage_id,
+    outroot
+    FROM distRun
+    JOIN stackSumSkyfile on stage_id = stack_id
+    LEFT JOIN distComponent
+        ON distRun.dist_id = distComponent.dist_id
+    WHERE
+        distRun.state = 'new'
+        AND distRun.fault = 0
+        AND distRun.stage = 'stack'
+        AND distComponent.component IS NOT NULL
+        AND distComponent.fault = 0
+) as Foo
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/share/pxadmin_create_tables.sql
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/share/pxadmin_create_tables.sql	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/share/pxadmin_create_tables.sql	(revision 23352)
@@ -737,7 +737,5 @@
     REFERENCES  detInputExp(det_id, iteration, exp_id),
     FOREIGN KEY (det_id, exp_id, class_id)
-    REFERENCES  detProcessedImfile(det_id, exp_id, class_id),
-    FOREIGN KEY (ref_det_id, ref_iter)
-    REFERENCES  detNormalizedExp(det_id, iteration)
+    REFERENCES  detProcessedImfile(det_id, exp_id, class_id)
 ) ENGINE=innodb DEFAULT CHARSET=latin1;
 
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/share/warptool_change_skyfile_data_state.sql
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/share/warptool_change_skyfile_data_state.sql	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/share/warptool_change_skyfile_data_state.sql	(revision 23352)
@@ -9,7 +9,5 @@
     AND skycell_id = '%s'
     -- only update if chipRun.state has the expected value
-    AND (
-        SELECT state from warpRun where warpRun.warp_id = warpSkyfile.warp_id
-    ) = '%s'
-    
-
+--    AND (
+--        SELECT state from warpRun where warpRun.warp_id = warpSkyfile.warp_id
+--    ) = '%s'
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/src/camtool.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/src/camtool.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/src/camtool.c	(revision 23352)
@@ -44,4 +44,6 @@
 static bool pendingcleanupexpMode(pxConfig *config);
 static bool donecleanupMode(pxConfig *config);
+static bool exportrunMode(pxConfig *config);
+static bool importrunMode(pxConfig *config);
 
 # define MODECASE(caseName, func) \
@@ -77,4 +79,6 @@
         MODECASE(CAMTOOL_MODE_PENDINGCLEANUPEXP,    pendingcleanupexpMode);
         MODECASE(CAMTOOL_MODE_DONECLEANUP,          donecleanupMode);
+        MODECASE(CAMTOOL_MODE_EXPORTRUN,            exportrunMode);
+        MODECASE(CAMTOOL_MODE_IMPORTRUN,            importrunMode);
         default:
             psAbort("invalid option (this should not happen)");
@@ -1081,2 +1085,127 @@
     return true;
 }
+
+bool exportrunMode(pxConfig *config)
+{
+  typedef struct ExportTable {
+    char tableName[80];
+    char sqlFilename[80];
+  } ExportTable;
+  
+  int numExportTables = 2;
+
+  PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+  PXOPT_LOOKUP_S64(det_id, config->args, "-cam_id", true,  false);
+  PXOPT_LOOKUP_STR(outfile, config->args, "-outfile", true,  false);
+  PXOPT_LOOKUP_U64(limit,   config->args, "-limit",   false, false);
+
+  FILE *f = fopen (outfile, "w");
+  if (f == NULL) {
+    psError(PS_ERR_UNKNOWN, false, "failed to open output file");
+    return false;
+  }
+
+  psMetadata *where = psMetadataAlloc();
+  PXOPT_COPY_S64(config->args, where, "-cam_id", "cam_id", "==");
+
+  ExportTable tables [] = {
+    {"camRun", "camtool_export_run.sql"},
+    {"camProcessedExp", "camtool_export_processed_exp.sql"},
+  };
+
+  for (int i=0; i < numExportTables; i++) {
+    psString query = pxDataGet(tables[i].sqlFilename);
+    if (!query) {
+      psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+      return false;
+    }
+
+    if (where && psListLength(where->list)) {
+      psString whereClause = psDBGenerateWhereSQL(where, NULL);
+      psStringAppend(&query, " %s", whereClause);
+      psFree(whereClause);
+    }
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+      psString limitString = psDBGenerateLimitSQL(limit);
+      psStringAppend(&query, " %s", limitString);
+      psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+      psError(PS_ERR_UNKNOWN, false, "database error");
+      psFree(query);
+      return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+      psError(PS_ERR_UNKNOWN, false, "database error");
+      return false;
+    }
+    if (!psArrayLength(output)) {
+      psTrace("regtool", PS_LOG_INFO, "no rows found");
+      psFree(output);
+      return true;
+    }
+
+    // we must write the export table in non-simple (true) format
+    if (!ippdbPrintMetadatas(f, output, tables[i].tableName, true)) {
+      psError(PS_ERR_UNKNOWN, false, "failed to print array");
+      psFree(output);
+      return false;
+    }
+    psFree(output);
+  }
+
+  fclose (f);
+
+  return true;
+}
+
+bool importrunMode(pxConfig *config)
+{
+  unsigned int nFail;
+
+  PS_ASSERT_PTR_NON_NULL(config, NULL);
+  
+  PXOPT_LOOKUP_STR(infile, config->args, "-infile", true,  false);
+
+  psMetadata *input = psMetadataConfigRead (NULL, &nFail, infile, false);
+
+  fprintf (stdout, "---- input ----\n");
+  psMetadataPrint (stderr, input, 1);
+
+  psMetadataItem *item = psMetadataLookup (input, "camRun");
+  psAssert (item, "entry not in input?");
+  psAssert (item->type == PS_DATA_METADATA_MULTI, "entry not multi?");
+  
+  psMetadataItem *entry = psListGet (item->data.list, 0);
+  assert (entry);
+  assert (entry->type == PS_DATA_METADATA);
+  camRunRow *camRun = camRunObjectFromMetadata (entry->data.md);
+  camRunInsertObject (config->dbh, camRun);
+
+  // fprintf (stdout, "---- cam run ----\n");
+  // psMetadataPrint (stderr, entry->data.md, 1);
+  
+  item = psMetadataLookup (input, "camProcessedImfile");
+  psAssert (item, "entry not in input?");
+  psAssert (item->type == PS_DATA_METADATA_MULTI, "entry not multi?");
+
+  for (int i = 0; i < item->data.list->n; i++) {
+    psMetadataItem *entry = psListGet (item->data.list, i);
+    assert (entry);
+    assert (entry->type == PS_DATA_METADATA);
+    camProcessedExpRow *camProcessedExp = camProcessedExpObjectFromMetadata (entry->data.md);
+    camProcessedExpInsertObject (config->dbh, camProcessedExp);
+
+    // fprintf (stdout, "---- row %d ----\n", i);
+    // psMetadataPrint (stderr, entry->data.md, 1);
+  }
+
+  return true;
+}
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/src/camtool.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/src/camtool.h	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/src/camtool.h	(revision 23352)
@@ -39,4 +39,6 @@
     CAMTOOL_MODE_PENDINGCLEANUPEXP,
     CAMTOOL_MODE_DONECLEANUP,
+    CAMTOOL_MODE_EXPORTRUN,
+    CAMTOOL_MODE_IMPORTRUN
 } camtoolMode;
 
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/src/camtoolConfig.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/src/camtoolConfig.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/src/camtoolConfig.c	(revision 23352)
@@ -228,4 +228,15 @@
     psMetadataAddU64(donecleanupArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
 
+    // -exportrun
+    psMetadata *exportrunArgs = psMetadataAlloc();
+    psMetadataAddS64(exportrunArgs, PS_LIST_TAIL, "-cam_id", 0,          "export this camera ID (required)", 0);
+    psMetadataAddStr(exportrunArgs, PS_LIST_TAIL, "-outfile", 0,          "export to this file (required)", NULL);
+    psMetadataAddU64(exportrunArgs, PS_LIST_TAIL, "-limit",   0,          "limit result set to N items", 0);
+
+    // -importrun
+    psMetadata *importrunArgs = psMetadataAlloc();
+    psMetadataAddStr(importrunArgs, PS_LIST_TAIL, "-infile",  0,          "import from this file (required)", NULL);
+
+
     psMetadata *argSets = psMetadataAlloc();
     psMetadata *modes = psMetadataAlloc();
@@ -245,4 +256,6 @@
     PXOPT_ADD_MODE("-pendingcleanupexp",    "show exposures for cleanup runs",      CAMTOOL_MODE_PENDINGCLEANUPEXP, pendingcleanupexpArgs);
     PXOPT_ADD_MODE("-donecleanup",          "show runs that have been cleaned",     CAMTOOL_MODE_DONECLEANUP,       donecleanupArgs);
+   PXOPT_ADD_MODE("-exportrun",            "export run for import on other database", CAMTOOL_MODE_EXPORTRUN, exportrunArgs);
+    PXOPT_ADD_MODE("-importrun",            "import run from metadata file",           CAMTOOL_MODE_IMPORTRUN, importrunArgs);
 
     if (!pxGetOptions(stderr, argc, argv, config, modes, argSets)) {
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/src/chiptool.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/src/chiptool.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/src/chiptool.c	(revision 23352)
@@ -42,5 +42,5 @@
 static bool revertprocessedimfileMode(pxConfig *config);
 static bool updateprocessedimfileMode(pxConfig *config);
-static bool promoteexpMode(pxConfig *config);
+static bool advanceexpMode(pxConfig *config);
 static bool blockMode(pxConfig *config);
 static bool maskedMode(pxConfig *config);
@@ -81,5 +81,5 @@
         MODECASE(CHIPTOOL_MODE_REVERTPROCESSEDIMFILE,   revertprocessedimfileMode);
         MODECASE(CHIPTOOL_MODE_UPDATEPROCESSEDIMFILE,   updateprocessedimfileMode);
-        MODECASE(CHIPTOOL_MODE_PROMOTEEXP,              promoteexpMode);
+        MODECASE(CHIPTOOL_MODE_ADVANCEEXP,              advanceexpMode);
         MODECASE(CHIPTOOL_MODE_BLOCK,                   blockMode);
         MODECASE(CHIPTOOL_MODE_MASKED,                  maskedMode);
@@ -239,5 +239,6 @@
     pxchipGetSearchArgs (config, where); // rawExp, chipRun
     PXOPT_COPY_S64(config->args,  where, "-chip_id", "chipRun.chip_id", "==");
-    PXOPT_COPY_STR(config->args,  where, "-label", "chipRun.label", "==");
+    PXOPT_COPY_STR(config->args,  where, "-label",   "chipRun.label",   "==");
+    PXOPT_COPY_STR(config->args,  where, "-state",   "chipRun.state",   "==");
 
     if (!psListLength(where->list)
@@ -1078,5 +1079,5 @@
 
 
-static bool promoteexpMode(pxConfig *config)
+static bool advanceexpMode(pxConfig *config)
 {
     PS_ASSERT_PTR_NON_NULL(config, false);
@@ -1256,43 +1257,57 @@
 bool exportrunMode(pxConfig *config)
 {
-    PS_ASSERT_PTR_NON_NULL(config, NULL);
-
-    PXOPT_LOOKUP_S64(chip_id, config->args, "-chip_id", true,  false);
-    PXOPT_LOOKUP_STR(outfile, config->args, "-outfile", true,  false);
-    PXOPT_LOOKUP_U64(limit,   config->args, "-limit",   false, false);
-
-    FILE *f = fopen (outfile, "w");
-    if (f == NULL) {
-        psError(PS_ERR_UNKNOWN, false, "failed to open output file");
-        return false;
-    }
-
-    psMetadata *where = psMetadataAlloc();
-    PXOPT_COPY_S64(config->args, where, "-chip_id", "chip_id", "==");
-
-    // *** extract the chipRun in this section ***
-    psString query = pxDataGet("chiptool_export_run.sql");
+  typedef struct ExportTable {
+    char tableName[80];
+    char sqlFilename[80];
+  } ExportTable;
+  
+  int numExportTables = 3;
+
+  PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+  PXOPT_LOOKUP_S64(det_id, config->args, "-chip_id", true,  false);
+  PXOPT_LOOKUP_STR(outfile, config->args, "-outfile", true,  false);
+  PXOPT_LOOKUP_U64(limit,   config->args, "-limit",   false, false);
+
+  FILE *f = fopen (outfile, "w");
+  if (f == NULL) {
+    psError(PS_ERR_UNKNOWN, false, "failed to open output file");
+    return false;
+  }
+
+  psMetadata *where = psMetadataAlloc();
+  PXOPT_COPY_S64(config->args, where, "-chip_id", "chip_id", "==");
+
+  ExportTable tables [] = {
+    {"chipRun", "chiptool_export_run.sql"},
+    {"chipImfile", "chiptool_export_imfile.sql"},
+    {"chipProcessedImfile", "chiptool_export_processed_imfile.sql"},
+  };
+
+
+  for (int i=0; i < numExportTables; i++) {
+    psString query = pxDataGet(tables[i].sqlFilename);
     if (!query) {
-        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
-        return false;
+      psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+      return false;
     }
 
     if (where && psListLength(where->list)) {
-        psString whereClause = psDBGenerateWhereSQL(where, NULL);
-        psStringAppend(&query, " %s", whereClause);
-        psFree(whereClause);
+      psString whereClause = psDBGenerateWhereSQL(where, NULL);
+      psStringAppend(&query, " %s", whereClause);
+      psFree(whereClause);
     }
 
     // treat limit == 0 as "no limit"
     if (limit) {
-        psString limitString = psDBGenerateLimitSQL(limit);
-        psStringAppend(&query, " %s", limitString);
-        psFree(limitString);
+      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;
+      psError(PS_ERR_UNKNOWN, false, "database error");
+      psFree(query);
+      return false;
     }
     psFree(query);
@@ -1300,77 +1315,34 @@
     psArray *output = p_psDBFetchResult(config->dbh);
     if (!output) {
-        psError(PS_ERR_UNKNOWN, false, "database error");
-        return false;
+      psError(PS_ERR_UNKNOWN, false, "database error");
+      return false;
     }
     if (!psArrayLength(output)) {
-        psTrace("chiptool", PS_LOG_INFO, "no rows found");
-        psFree(output);
-        return true;
+      psTrace("regtool", PS_LOG_INFO, "no rows found");
+      psFree(output);
+      return true;
     }
 
     // we must write the export table in non-simple (true) format
-    if (!ippdbPrintMetadatas(f, output, "chipRun", true)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to print array");
-        psFree(output);
-        return false;
+    if (!ippdbPrintMetadatas(f, output, tables[i].tableName, true)) {
+      psError(PS_ERR_UNKNOWN, false, "failed to print array");
+      psFree(output);
+      return false;
     }
     psFree(output);
-
-    // *** extract the chipProcessedImfile entries in this section ***
-    query = pxDataGet("chiptool_export_imfiles.sql");
-    if (!query) {
-        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
-        return false;
-    }
-
-    if (where && psListLength(where->list)) {
-        psString whereClause = psDBGenerateWhereSQL(where, NULL);
-        psStringAppend(&query, " %s", whereClause);
-        psFree(whereClause);
-    }
-    psFree(where);
-
-    // treat limit == 0 as "no limit"
-    if (limit) {
-        psString limitString = psDBGenerateLimitSQL(limit);
-        psStringAppend(&query, " %s", limitString);
-        psFree(limitString);
-    }
-
-    if (!p_psDBRunQuery(config->dbh, query)) {
-        psError(PS_ERR_UNKNOWN, false, "database error");
-        psFree(query);
-        return false;
-    }
-    psFree(query);
-
-    output = p_psDBFetchResult(config->dbh);
-    if (!output) {
-        psError(PS_ERR_UNKNOWN, false, "database error");
-        return false;
-    }
-    if (!psArrayLength(output)) {
-        psTrace("chiptool", PS_LOG_INFO, "no rows found");
-        psFree(output);
-        return true;
-    }
-
-    // we must write the export table in non-simple (true) format
-    if (!ippdbPrintMetadatas(f, output, "chipProcessedImfiles", true)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to print array");
-        psFree(output);
-        return false;
-    }
-    psFree(output);
-
-    fclose (f);
-
-    return true;
-}
-
+  }
+
+  fclose (f);
+
+  return true;
+}
 
 bool importrunMode(pxConfig *config)
 {
   unsigned int nFail;
+  
+  int numImportTables = 2;
+ 
+  char tables[2] [80] = {"chipImfile", "chipProcessedImfile"};
 
   PS_ASSERT_PTR_NON_NULL(config, NULL);
@@ -1383,31 +1355,49 @@
   psMetadataPrint (stderr, input, 1);
 
-  psMetadataItem *chipRunItem = psMetadataLookup (input, "chipRun");
-  psAssert (chipRunItem, "entry not in input?");
-  psAssert (chipRunItem->type == PS_DATA_METADATA_MULTI, "entry not multi?");
-  
-  psMetadataItem *chipRunEntry = psListGet (chipRunItem->data.list, 0);
-  assert (chipRunEntry);
-  assert (chipRunEntry->type == PS_DATA_METADATA);
-  chipRunRow *chipRun = chipRunObjectFromMetadata (chipRunEntry->data.md);
-  chipRunInsertObject (config->dbh, chipRun);
-
-  // fprintf (stdout, "---- chip run ----\n");
-  // psMetadataPrint (stderr, chipRunEntry->data.md, 1);
-
-  psMetadataItem *item = psMetadataLookup (input, "chipProcessedImfiles");
+  psMetadataItem *item = psMetadataLookup (input, "chipRun");
   psAssert (item, "entry not in input?");
   psAssert (item->type == PS_DATA_METADATA_MULTI, "entry not multi?");
 
-  // XXX would be better to use the iterator?
-  for (int i = 0; i < item->data.list->n; i++) {
-    psMetadataItem *entry = psListGet (item->data.list, i);
-    assert (entry);
-    assert (entry->type == PS_DATA_METADATA);
-    chipProcessedImfileRow *chipProcessedImfile = chipProcessedImfileObjectFromMetadata (entry->data.md);
-    chipProcessedImfileInsertObject (config->dbh, chipProcessedImfile);
-
-    // fprintf (stdout, "---- row %d ----\n", i);
-    // psMetadataPrint (stderr, entry->data.md, 1);
+  psMetadataItem *entry = psListGet (item->data.list, 0);
+  assert (entry);
+  assert (entry->type == PS_DATA_METADATA);
+  chipRunRow *chipRun = chipRunObjectFromMetadata (entry->data.md);
+  chipRunInsertObject (config->dbh, chipRun);
+
+  // fprintf (stdout, "---- chip run ----\n");
+  // psMetadataPrint (stderr, entry->data.md, 1);
+  
+  for (int i = 0; i < numImportTables; i++) {
+    psMetadataItem *item = psMetadataLookup (input, tables[i]);
+    psAssert (item, "entry not in input?");
+    psAssert (item->type == PS_DATA_METADATA_MULTI, "entry not multi?");
+  
+    switch (i) {
+      case 0:
+        for (int i = 0; i < item->data.list->n; i++) {
+          psMetadataItem *entry = psListGet (item->data.list, i);
+          assert (entry);
+          assert (entry->type == PS_DATA_METADATA);
+          chipImfileRow *chipImfile = chipImfileObjectFromMetadata (entry->data.md);
+          chipImfileInsertObject (config->dbh, chipImfile);
+
+          // fprintf (stdout, "---- row %d ----\n", i);
+          // psMetadataPrint (stderr, entry->data.md, 1);
+        }
+        break;
+        
+      case 1:
+        for (int i = 0; i < item->data.list->n; i++) {
+          psMetadataItem *entry = psListGet (item->data.list, i);
+          assert (entry);
+          assert (entry->type == PS_DATA_METADATA);
+          chipProcessedImfileRow *chipProcessedImfile = chipProcessedImfileObjectFromMetadata (entry->data.md);
+          chipProcessedImfileInsertObject (config->dbh, chipProcessedImfile);
+
+          // fprintf (stdout, "---- row %d ----\n", i);
+          // psMetadataPrint (stderr, entry->data.md, 1);
+        }
+        break;
+    }
   }
 
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/src/chiptool.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/src/chiptool.h	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/src/chiptool.h	(revision 23352)
@@ -32,5 +32,5 @@
     CHIPTOOL_MODE_REVERTPROCESSEDIMFILE,
     CHIPTOOL_MODE_UPDATEPROCESSEDIMFILE,
-    CHIPTOOL_MODE_PROMOTEEXP,
+    CHIPTOOL_MODE_ADVANCEEXP,
     CHIPTOOL_MODE_BLOCK,
     CHIPTOOL_MODE_MASKED,
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/src/chiptoolConfig.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/src/chiptoolConfig.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/src/chiptoolConfig.c	(revision 23352)
@@ -67,8 +67,8 @@
     psMetadataAddS64(updaterunArgs, PS_LIST_TAIL, "-chip_id",              0,            "search by chip ID", 0);
     psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-state", 0,            "set state", NULL);
+    psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-label",  0,          "search by label", NULL);
     psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-set_state", 0,        "set state", NULL);
     psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-set_label", 0,        "set label", NULL);
     psMetadataAddBool(updaterunArgs, PS_LIST_TAIL, "-all",  0,            "allow everything to be queued without search terms", false);
-    psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-label",  0,          "search by label", NULL);
 
     // -pendingimfile
@@ -176,9 +176,9 @@
     psMetadataAddS16(updateprocessedimfileArgs, PS_LIST_TAIL, "-code",  0,            "set fault code (required)", 0);
 
-    // -promoteexp
-    psMetadata *promoteexpArgs = psMetadataAlloc();
-    psMetadataAddS64(promoteexpArgs, PS_LIST_TAIL, "-chip_id",  0,          "search by chip ID", 0);
-    psMetadataAddStr(promoteexpArgs, PS_LIST_TAIL, "-label",  0,            "promote exposures for specified label", NULL);
-    psMetadataAddU64(promoteexpArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+    // -advanceexp
+    psMetadata *advanceexpArgs = psMetadataAlloc();
+    psMetadataAddS64(advanceexpArgs, PS_LIST_TAIL, "-chip_id",  0,          "search by chip ID", 0);
+    psMetadataAddStr(advanceexpArgs, PS_LIST_TAIL, "-label",  0,            "advance exposures for specified label", NULL);
+    psMetadataAddU64(advanceexpArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
 
     // -block
@@ -265,5 +265,5 @@
     PXOPT_ADD_MODE("-updateprocessedimfile","change procesed imfile properties",    CHIPTOOL_MODE_UPDATEPROCESSEDIMFILE,updateprocessedimfileArgs);
     PXOPT_ADD_MODE("-revertprocessedimfile","undo a processed imfile",              CHIPTOOL_MODE_REVERTPROCESSEDIMFILE,revertprocessedimfileArgs);
-    PXOPT_ADD_MODE("-promoteexp",           "promote completed exposures",          CHIPTOOL_MODE_PROMOTEEXP,           promoteexpArgs);
+    PXOPT_ADD_MODE("-advanceexp",           "advance completed exposures",          CHIPTOOL_MODE_ADVANCEEXP,           advanceexpArgs);
     PXOPT_ADD_MODE("-block",                "set a label block",                    CHIPTOOL_MODE_BLOCK,                blockArgs);
     PXOPT_ADD_MODE("-masked",               "show blocked labels",                  CHIPTOOL_MODE_MASKED,               maskedArgs);
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/src/dettool.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/src/dettool.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/src/dettool.c	(revision 23352)
@@ -28,4 +28,6 @@
 static bool inputMode(pxConfig *config);
 static bool rawMode(pxConfig *config);
+static bool exportrunMode(pxConfig *config);
+static bool importrunMode(pxConfig *config);
 
 // run
@@ -46,5 +48,10 @@
     } \
     break;
-
+/*
+typedef struct ExportTable {
+    char tableName[80];
+    char sqlFilename[80];
+} ExportTable;
+*/
 int main(int argc, char **argv)
 {
@@ -146,4 +153,6 @@
         MODECASE(DETTOOL_MODE_REGISTER_DETREND, register_detrendMode);
         MODECASE(DETTOOL_MODE_REGISTER_DETREND_IMFILE, register_detrend_imfileMode);
+        MODECASE(DETTOOL_MODE_EXPORTRUN,               exportrunMode);
+        MODECASE(DETTOOL_MODE_IMPORTRUN,               importrunMode);
         default:
             psAbort("invalid option (this should not happen)");
@@ -578,5 +587,9 @@
       // generate a random-valued vector, return an index sorted by the random values
       psVector *randomVector = psVectorAlloc(detrendExps->n, PS_TYPE_F32); // random values
-      psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 0);
+      /*
+       * change due to PAP work on random number generator?
+       * psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 0);
+       */
+      psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS);
       for (int i = 0; i < randomVector->n; i++) {
         randomVector->data.F32[i] = psRandomUniform(rng);
@@ -1996,4 +2009,283 @@
 }
 
+bool exportrunMode(pxConfig *config)
+{
+  typedef struct ExportTable {
+    char tableName[80];
+    char sqlFilename[80];
+  } ExportTable;
+  
+  int numExportTables = 12;
+
+  PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+  PXOPT_LOOKUP_S64(det_id, config->args, "-det_id", true,  false);
+  PXOPT_LOOKUP_STR(outfile, config->args, "-outfile", true,  false);
+  PXOPT_LOOKUP_U64(limit,   config->args, "-limit",   false, false);
+
+  FILE *f = fopen (outfile, "w");
+  if (f == NULL) {
+    psError(PS_ERR_UNKNOWN, false, "failed to open output file");
+    return false;
+  }
+
+  psMetadata *where = psMetadataAlloc();
+  PXOPT_COPY_S64(config->args, where, "-det_id", "det_id", "==");
+
+  ExportTable tables [] = {
+    {"detInputExp", "dettool_export_input_exp.sql"},
+    {"detNormalizedExp", "dettool_export_normalized_exp.sql"},
+    {"detNormalizedImfile", "dettool_export_normalized_imfile.sql"},
+    {"detNormalizedStatImfile", "dettool_export_normalized_stat_imfile.sql"},
+    {"detProcessedExp", "dettool_export_processed_exp.sql"},
+    {"detProcessedImfile", "dettool_export_processed_imfile.sql"},
+    {"detRegisteredImfile", "dettool_export_registered_imfile.sql"},
+    {"detResidExp", "dettool_export_resid_exp.sql"},
+    {"detResidImfile", "dettool_export_resid_imfile.sql"},
+    {"detRun", "dettool_export_run.sql"},
+    {"detRunSummary", "dettool_export_run_summary.sql"},
+    {"detStackedImfile", "dettool_export_stacked_imfile.sql"},
+  };
+
+  for (int i=0; i < numExportTables; i++) {
+    psString query = pxDataGet(tables[i].sqlFilename);
+    if (!query) {
+      psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+      return false;
+    }
+
+    if (where && psListLength(where->list)) {
+      psString whereClause = psDBGenerateWhereSQL(where, NULL);
+      psStringAppend(&query, " %s", whereClause);
+      psFree(whereClause);
+    }
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+      psString limitString = psDBGenerateLimitSQL(limit);
+      psStringAppend(&query, " %s", limitString);
+      psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+      psError(PS_ERR_UNKNOWN, false, "database error");
+      psFree(query);
+      return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+      psError(PS_ERR_UNKNOWN, false, "database error");
+      return false;
+    }
+    if (!psArrayLength(output)) {
+      psTrace("chiptool", PS_LOG_INFO, "no rows found");
+      psFree(output);
+      return true;
+    }
+
+    // we must write the export table in non-simple (true) format
+    if (!ippdbPrintMetadatas(f, output, tables[i].tableName, true)) {
+      psError(PS_ERR_UNKNOWN, false, "failed to print array");
+      psFree(output);
+      return false;
+    }
+    psFree(output);
+  
+  }
+
+  fclose (f);
+
+  return true;
+}
+
+bool importrunMode(pxConfig *config)
+{
+  unsigned int nFail;
+  
+  int numImportTables = 11;
+  
+  char tables[11] [80] = {"detInputExp", "detNormalizedExp",
+    "detNormalizedStatImfile", "detProcessedExp", "detProcessedImfile",
+    "detRegisteredImfile", "detResidExp", "detResidImfile",
+    "detNormalizedImfile", "detRunSummary", "detStackedImfile"};
+
+  PS_ASSERT_PTR_NON_NULL(config, NULL);
+  
+  PXOPT_LOOKUP_STR(infile, config->args, "-infile", true,  false);
+
+  psMetadata *input = psMetadataConfigRead (NULL, &nFail, infile, false);
+
+  fprintf (stdout, "---- input ----\n");
+  psMetadataPrint (stderr, input, 1);
+  
+  psMetadataItem *item = psMetadataLookup (input, "detRun");
+  psAssert (item, "entry not in input?");
+  psAssert (item->type == PS_DATA_METADATA_MULTI, "entry not multi?");
+
+  psMetadataItem *entry = psListGet (item->data.list, 0);
+  assert (entry);
+  assert (entry->type == PS_DATA_METADATA);
+  detRunRow *detRun = detRunObjectFromMetadata (entry->data.md);
+  detRunInsertObject (config->dbh, detRun);
+  
+  // fprintf (stdout, "---- det run ----\n");
+  // psMetadataPrint (stderr, entry->data.md, 1);
+
+  for (int i = 0; i < numImportTables; i++) {
+    item = psMetadataLookup (input, tables[i]);
+    psAssert (item, "entry not in input?");
+    psAssert (item->type == PS_DATA_METADATA_MULTI, "entry not multi?");
+  
+    switch (i) {
+      case 0:
+        for (int i = 0; i < item->data.list->n; i++) {
+          entry = psListGet (item->data.list, i);
+          assert (entry);
+          assert (entry->type == PS_DATA_METADATA);
+          detInputExpRow *detInputExp = detInputExpObjectFromMetadata (entry->data.md);
+          detInputExpInsertObject (config->dbh, detInputExp);
+
+          // fprintf (stdout, "---- row %d ----\n", i);
+          // psMetadataPrint (stderr, entry->data.md, 1);
+        }
+        break;
+  
+      case 1:
+        for (int i = 0; i < item->data.list->n; i++) {
+          entry = psListGet (item->data.list, i);
+          assert (entry);
+          assert (entry->type == PS_DATA_METADATA);
+          detNormalizedExpRow *detNormalizedExp = detNormalizedExpObjectFromMetadata (entry->data.md);
+          detNormalizedExpInsertObject (config->dbh, detNormalizedExp);
+
+          // fprintf (stdout, "---- row %d ----\n", i);
+          // psMetadataPrint (stderr, entry->data.md, 1);
+        }
+        break;
+        
+      case 2:
+        for (int i = 0; i < item->data.list->n; i++) {
+          entry = psListGet (item->data.list, i);
+          assert (entry);
+          assert (entry->type == PS_DATA_METADATA);
+          detNormalizedStatImfileRow *detNormalizedStatImfile = detNormalizedStatImfileObjectFromMetadata (entry->data.md);
+          detNormalizedStatImfileInsertObject (config->dbh, detNormalizedStatImfile);
+
+          // fprintf (stdout, "---- row %d ----\n", i);
+          // psMetadataPrint (stderr, entry->data.md, 1);
+        }
+        break;
+ 
+      case 3:
+        for (int i = 0; i < item->data.list->n; i++) {
+          entry = psListGet (item->data.list, i);
+          assert (entry);
+          assert (entry->type == PS_DATA_METADATA);
+          detProcessedExpRow *detProcessedExp =  detProcessedExpObjectFromMetadata (entry->data.md);
+          detProcessedExpInsertObject (config->dbh, detProcessedExp);
+
+          // fprintf (stdout, "---- row %d ----\n", i);
+          // psMetadataPrint (stderr, entry->data.md, 1);
+        }
+        break;
+        
+      case 4:
+        for (int i = 0; i < item->data.list->n; i++) {
+          entry = psListGet (item->data.list, i);
+          assert (entry);
+          assert (entry->type == PS_DATA_METADATA);
+          detProcessedImfileRow *detProcessedImfile =  detProcessedImfileObjectFromMetadata (entry->data.md);
+          detProcessedImfileInsertObject (config->dbh, detProcessedImfile);
+
+          // fprintf (stdout, "---- row %d ----\n", i);
+          // psMetadataPrint (stderr, entry->data.md, 1);
+        }
+        break;
+        
+      case 5:
+        for (int i = 0; i < item->data.list->n; i++) {
+          entry = psListGet (item->data.list, i);
+          assert (entry);
+          assert (entry->type == PS_DATA_METADATA);
+          detRegisteredImfileRow *detRegisteredImfile = detRegisteredImfileObjectFromMetadata (entry->data.md);
+          detRegisteredImfileInsertObject (config->dbh, detRegisteredImfile);
+
+          // fprintf (stdout, "---- row %d ----\n", i);
+          // psMetadataPrint (stderr, entry->data.md, 1);
+        }
+        break;
+        
+      case 6:
+        for (int i = 0; i < item->data.list->n; i++) {
+          entry = psListGet (item->data.list, i);
+          assert (entry);
+          assert (entry->type == PS_DATA_METADATA);
+          detResidExpRow *detResidExp = detResidExpObjectFromMetadata (entry->data.md);
+          detResidExpInsertObject (config->dbh, detResidExp);
+
+          // fprintf (stdout, "---- row %d ----\n", i);
+          // psMetadataPrint (stderr, entry->data.md, 1);
+        }
+        break;
+        
+      case 7:
+        for (int i = 0; i < item->data.list->n; i++) {
+          entry = psListGet (item->data.list, i);
+          assert (entry);
+          assert (entry->type == PS_DATA_METADATA);
+          detResidImfileRow *detResidImfile = detResidImfileObjectFromMetadata (entry->data.md);
+          detResidImfileInsertObject (config->dbh, detResidImfile);
+
+          // fprintf (stdout, "---- row %d ----\n", i);
+          // psMetadataPrint (stderr, entry->data.md, 1);
+        }
+        break;
+        
+      case 8:
+        for (int i = 0; i < item->data.list->n; i++) {
+          entry = psListGet (item->data.list, i);
+          assert (entry);
+          assert (entry->type == PS_DATA_METADATA);
+          detNormalizedImfileRow *detNormalizedImfile = detNormalizedImfileObjectFromMetadata (entry->data.md);
+          detNormalizedImfileInsertObject (config->dbh, detNormalizedImfile);
+
+          // fprintf (stdout, "---- row %d ----\n", i);
+          // psMetadataPrint (stderr, entry->data.md, 1);
+        }
+        break;
+        
+      case 9:
+        for (int i = 0; i < item->data.list->n; i++) {
+          entry = psListGet (item->data.list, i);
+          assert (entry);
+          assert (entry->type == PS_DATA_METADATA);
+          detRunSummaryRow *detRunSummary = detRunSummaryObjectFromMetadata (entry->data.md);
+          detRunSummaryInsertObject (config->dbh, detRunSummary);
+
+          // fprintf (stdout, "---- row %d ----\n", i);
+          // psMetadataPrint (stderr, entry->data.md, 1);
+        }
+        break;
+        
+      case 10:
+        for (int i = 0; i < item->data.list->n; i++) {
+          entry = psListGet (item->data.list, i);
+          assert (entry);
+          assert (entry->type == PS_DATA_METADATA);
+          detStackedImfileRow *detStackedImfile = detStackedImfileObjectFromMetadata (entry->data.md);
+          detStackedImfileInsertObject (config->dbh, detStackedImfile);
+
+          // fprintf (stdout, "---- row %d ----\n", i);
+          // psMetadataPrint (stderr, entry->data.md, 1);
+        }
+        break;
+    }
+  }
+
+  return true;
+}
+
 #if 0
 // XXX this function was left in commented as this method may be useful in the
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/src/dettool.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/src/dettool.h	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/src/dettool.h	(revision 23352)
@@ -114,5 +114,8 @@
     DETTOOL_MODE_RERUN,
     DETTOOL_MODE_REGISTER_DETREND,
-    DETTOOL_MODE_REGISTER_DETREND_IMFILE
+    DETTOOL_MODE_REGISTER_DETREND_IMFILE,
+
+    DETTOOL_MODE_EXPORTRUN,
+    DETTOOL_MODE_IMPORTRUN
 } dettoolMode;
 
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/src/dettoolConfig.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/src/dettoolConfig.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/src/dettoolConfig.c	(revision 23352)
@@ -648,4 +648,6 @@
     psMetadataAddBool(residimfileArgs, PS_LIST_TAIL, "-faulted",  0,            "only return imfiles with a fault status set", false);
     psMetadataAddBool(residimfileArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+    psMetadataAddBool(residimfileArgs, PS_LIST_TAIL, "-included",  0,            "use only files included in ths detrun", false);
+    // XXX is this option used?
     psMetadataAddStr(residimfileArgs, PS_LIST_TAIL, "-select_state",  0,            "search for state", NULL);
 
@@ -855,4 +857,15 @@
     psMetadataAddStr(register_detrend_imfileArgs, PS_LIST_TAIL, "-path_base",  0,            "define base output location", NULL);
 
+    // -exportrun
+    psMetadata *exportrunArgs = psMetadataAlloc();
+    psMetadataAddS64(exportrunArgs, PS_LIST_TAIL, "-det_id", 0,          "export this detrend ID (required)", 0);
+    psMetadataAddStr(exportrunArgs, PS_LIST_TAIL, "-outfile", 0,          "export to this file (required)", NULL);
+    psMetadataAddU64(exportrunArgs, PS_LIST_TAIL, "-limit",   0,          "limit result set to N items", 0);
+
+    // -importrun
+    psMetadata *importrunArgs = psMetadataAlloc();
+    psMetadataAddStr(importrunArgs, PS_LIST_TAIL, "-infile",  0,          "import from this file (required)", NULL);
+
+
     psFree(now);
 
@@ -947,4 +960,6 @@
     PXOPT_ADD_MODE("-register_detrend", "", DETTOOL_MODE_REGISTER_DETREND, register_detrendArgs);
     PXOPT_ADD_MODE("-register_detrend_imfile", "", DETTOOL_MODE_REGISTER_DETREND_IMFILE, register_detrend_imfileArgs);
+    PXOPT_ADD_MODE("-exportrun",            "export run for import on other database", DETTOOL_MODE_EXPORTRUN, exportrunArgs);
+    PXOPT_ADD_MODE("-importrun",            "import run from metadata file",           DETTOOL_MODE_IMPORTRUN, importrunArgs);
 
     if (!pxGetOptions(stderr, argc, argv, config, modes, argSets)) {
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/src/dettool_processedimfile.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/src/dettool_processedimfile.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/src/dettool_processedimfile.c	(revision 23352)
@@ -165,11 +165,4 @@
     PS_ASSERT_PTR_NON_NULL(config, false);
 
-    bool hasWhere = false;
-
-    PXOPT_LOOKUP_BOOL(included, config->args, "-included", false);
-    PXOPT_LOOKUP_BOOL(faulted, config->args, "-faulted", false);
-    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
-    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
-
     psMetadata *where = psMetadataAlloc();
     PXOPT_COPY_S64(config->args, where, "-det_id", "detProcessedImfile.det_id", "==");
@@ -179,4 +172,10 @@
     PXOPT_COPY_STR(config->args, where, "-select_mode", "detRun.mode", "==");
 
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    PXOPT_LOOKUP_BOOL(faulted, config->args, "-faulted", false);
+    PXOPT_LOOKUP_BOOL(included, config->args, "-included", false);
+
     psString query = pxDataGet("dettool_processedimfile.sql");
     if (!query) {
@@ -186,4 +185,5 @@
     }
 
+    bool hasWhere = false;
     if (psListLength(where->list)) {
         psString whereClause = psDBGenerateWhereSQL(where, NULL);
@@ -211,4 +211,5 @@
 
     if (faulted) {
+        // list only faulted rows
 	psStringAppend(&query, " %s", " detProcessedImfile.fault != 0");
     } else {
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/src/dettool_residimfile.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/src/dettool_residimfile.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/src/dettool_residimfile.c	(revision 23352)
@@ -159,5 +159,7 @@
     PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
     PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
     PXOPT_LOOKUP_BOOL(faulted, config->args, "-faulted", false);
+    PXOPT_LOOKUP_BOOL(included, config->args, "-included", false);
 
     psString query = pxDataGet("dettool_residimfile.sql");
@@ -175,4 +177,14 @@
     }
     psFree(where);
+
+    // restrict search to included imfiles
+    if (included) {
+	if (hasWhere) {
+	    psStringAppend(&query, " AND detInputExp.include = 1");
+	} else {
+	    psStringAppend(&query, " WHERE detInputExp.include = 1");
+	}
+	hasWhere = true;
+    }
 
     if (hasWhere) {
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/src/difftool.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/src/difftool.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/src/difftool.c	(revision 23352)
@@ -45,4 +45,6 @@
 static bool donecleanupMode(pxConfig *config);
 static bool updatediffskyfileMode(pxConfig *config);
+static bool exportrunMode(pxConfig *config);
+static bool importrunMode(pxConfig *config);
 
 static bool setdiffRunState(pxConfig *config, psS64 diff_id, const char *state);
@@ -81,4 +83,6 @@
         MODECASE(DIFFTOOL_MODE_DONECLEANUP,           donecleanupMode);
         MODECASE(DIFFTOOL_MODE_UPDATEDIFFSKYFILE,     updatediffskyfileMode);
+        MODECASE(DIFFTOOL_MODE_EXPORTRUN,             exportrunMode);
+        MODECASE(DIFFTOOL_MODE_IMPORTRUN,             importrunMode);
         default:
             psAbort("invalid option (this should not happen)");
@@ -1437,2 +1441,150 @@
 }
 
+bool exportrunMode(pxConfig *config)
+{
+  typedef struct ExportTable {
+    char tableName[80];
+    char sqlFilename[80];
+  } ExportTable;
+  
+  int numExportFiles = 3;
+
+  PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+  PXOPT_LOOKUP_S64(det_id, config->args, "-diff_id", true,  false);
+  PXOPT_LOOKUP_STR(outfile, config->args, "-outfile", true,  false);
+  PXOPT_LOOKUP_U64(limit,   config->args, "-limit",   false, false);
+
+  FILE *f = fopen (outfile, "w");
+  if (f == NULL) {
+    psError(PS_ERR_UNKNOWN, false, "failed to open output file");
+    return false;
+  }
+
+  psMetadata *where = psMetadataAlloc();
+  PXOPT_COPY_S64(config->args, where, "-diff_id", "diff_id", "==");
+
+  ExportTable tables [] = {
+    {"diffRun", "difftool_export_run.sql"},
+    {"diffInputSkyfile", "difftool_export_input_skyfile.sql"},
+    {"diffSkyfile", "difftool_export_skyfile.sql"},
+  };
+
+  for (int i=0; i < numExportFiles; i++) {
+    psString query = pxDataGet(tables[i].sqlFilename);
+    if (!query) {
+      psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+      return false;
+    }
+
+    if (where && psListLength(where->list)) {
+      psString whereClause = psDBGenerateWhereSQL(where, NULL);
+      psStringAppend(&query, " %s", whereClause);
+      psFree(whereClause);
+    }
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+      psString limitString = psDBGenerateLimitSQL(limit);
+      psStringAppend(&query, " %s", limitString);
+      psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+      psError(PS_ERR_UNKNOWN, false, "database error");
+      psFree(query);
+      return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+      psError(PS_ERR_UNKNOWN, false, "database error");
+      return false;
+    }
+    if (!psArrayLength(output)) {
+      psTrace("regtool", PS_LOG_INFO, "no rows found");
+      psFree(output);
+      return true;
+    }
+
+    // we must write the export table in non-simple (true) format
+    if (!ippdbPrintMetadatas(f, output, tables[i].tableName, true)) {
+      psError(PS_ERR_UNKNOWN, false, "failed to print array");
+      psFree(output);
+      return false;
+    }
+    psFree(output);
+  }
+
+  fclose (f);
+
+  return true;
+}
+
+bool importrunMode(pxConfig *config)
+{
+  unsigned int nFail;
+  
+  int numImportTables = 2;
+  
+  char tables[2] [80] = {"diffInputSkyfile", "diffSkyfile"};
+
+  PS_ASSERT_PTR_NON_NULL(config, NULL);
+  
+  PXOPT_LOOKUP_STR(infile, config->args, "-infile", true,  false);
+
+  psMetadata *input = psMetadataConfigRead (NULL, &nFail, infile, false);
+
+  fprintf (stdout, "---- input ----\n");
+  psMetadataPrint (stderr, input, 1);
+
+  psMetadataItem *item = psMetadataLookup (input, "diffRun");
+  psAssert (item, "entry not in input?");
+  psAssert (item->type == PS_DATA_METADATA_MULTI, "entry not multi?");
+
+  psMetadataItem *entry = psListGet (item->data.list, 0);
+  assert (entry);
+  assert (entry->type == PS_DATA_METADATA);
+  diffRunRow *diffRun = diffRunObjectFromMetadata (entry->data.md);
+  diffRunInsertObject (config->dbh, diffRun);
+
+  // fprintf (stdout, "---- diff run ----\n");
+  // psMetadataPrint (stderr, entry->data.md, 1);
+
+  for (int i = 0; i < numImportTables; i++) {
+    item = psMetadataLookup (input, tables[i]);
+    psAssert (item, "entry not in input?");
+    psAssert (item->type == PS_DATA_METADATA_MULTI, "entry not multi?");
+  
+    switch (i) {
+      case 0:
+        for (int i = 0; i < item->data.list->n; i++) {
+          entry = psListGet (item->data.list, i);
+          assert (entry);
+          assert (entry->type == PS_DATA_METADATA);
+          diffInputSkyfileRow *diffInputSkyfile = diffInputSkyfileObjectFromMetadata (entry->data.md);
+          diffInputSkyfileInsertObject (config->dbh, diffInputSkyfile);
+
+          // fprintf (stdout, "---- row %d ----\n", i);
+          // psMetadataPrint (stderr, entry->data.md, 1);
+        }
+        break;
+        
+      case 1:
+        for (int i = 0; i < item->data.list->n; i++) {
+          entry = psListGet (item->data.list, i);
+          assert (entry);
+          assert (entry->type == PS_DATA_METADATA);
+          diffSkyfileRow *diffSkyfile = diffSkyfileObjectFromMetadata (entry->data.md);
+          diffSkyfileInsertObject (config->dbh, diffSkyfile);
+
+          // fprintf (stdout, "---- row %d ----\n", i);
+          // psMetadataPrint (stderr, entry->data.md, 1);
+        }
+        break;
+    }
+  }
+
+  return true;
+}
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/src/difftool.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/src/difftool.h	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/src/difftool.h	(revision 23352)
@@ -39,4 +39,6 @@
     DIFFTOOL_MODE_DONECLEANUP,
     DIFFTOOL_MODE_UPDATEDIFFSKYFILE,
+    DIFFTOOL_MODE_EXPORTRUN,
+    DIFFTOOL_MODE_IMPORTRUN
 } difftoolMode;
 
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/src/difftoolConfig.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/src/difftoolConfig.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/src/difftoolConfig.c	(revision 23352)
@@ -187,4 +187,15 @@
     psMetadataAddS16(updatediffskyfileArgs, PS_LIST_TAIL, "-code", 0,         "set fault code (required)", 0);
 
+    // -exportrun
+    psMetadata *exportrunArgs = psMetadataAlloc();
+    psMetadataAddS64(exportrunArgs, PS_LIST_TAIL, "-diff_id", 0,          "export this diff ID (required)", 0);
+    psMetadataAddStr(exportrunArgs, PS_LIST_TAIL, "-outfile", 0,          "export to this file (required)", NULL);
+    psMetadataAddU64(exportrunArgs, PS_LIST_TAIL, "-limit",   0,          "limit result set to N items", 0);
+
+    // -importrun
+    psMetadata *importrunArgs = psMetadataAlloc();
+    psMetadataAddStr(importrunArgs, PS_LIST_TAIL, "-infile",  0,          "import from this file (required)", NULL);
+
+
 
     psFree(now);
@@ -207,4 +218,6 @@
     PXOPT_ADD_MODE("-donecleanup",           "show runs that have been cleaned",     DIFFTOOL_MODE_DONECLEANUP,          donecleanupArgs);
     PXOPT_ADD_MODE("-updatediffskyfile",     "update fault code for a diffskyfile",  DIFFTOOL_MODE_UPDATEDIFFSKYFILE,          updatediffskyfileArgs);
+    PXOPT_ADD_MODE("-exportrun",            "export run for import on other database", DIFFTOOL_MODE_EXPORTRUN, exportrunArgs);
+    PXOPT_ADD_MODE("-importrun",            "import run from metadata file",           DIFFTOOL_MODE_IMPORTRUN, importrunArgs);
 
     if (!pxGetOptions(stderr, argc, argv, config, modes, argSets)) {
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/src/disttool.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/src/disttool.c	(revision 23352)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/src/disttool.c	(revision 23352)
@@ -0,0 +1,532 @@
+/*
+ * disttool.c
+ *
+ * Copyright (C) 2008
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <inttypes.h>
+
+#include "pxtools.h"
+#include "pxdata.h"
+#include "disttool.h"
+
+static bool definebyqueryMode(pxConfig *config);
+static bool definerunMode(pxConfig *config);
+static bool updaterunMode(pxConfig *config);
+static bool revertrunMode(pxConfig *config);
+static bool pendingcomponentMode(pxConfig *config);
+static bool addprocessedcomponentMode(pxConfig *config);
+static bool processedcomponentMode(pxConfig *config);
+static bool toadvanceMode(pxConfig *config);
+
+# define MODECASE(caseName, func) \
+    case caseName: \
+    if (!func(config)) { \
+                goto FAIL; \
+            } \
+    break;
+
+
+int main(int argc, char **argv)
+{
+    psLibInit(NULL);
+
+    pxConfig *config = disttoolConfig(NULL, argc, argv);
+    if (!config) {
+        psError(PXTOOLS_ERR_CONFIG, false, "failed to configure");
+        goto FAIL;
+    }
+
+    switch (config->mode) {
+        MODECASE(DISTTOOL_MODE_DEFINEBYQUERY, definebyqueryMode);
+        MODECASE(DISTTOOL_MODE_DEFINERUN, definerunMode);
+        MODECASE(DISTTOOL_MODE_UPDATERUN, updaterunMode);
+        MODECASE(DISTTOOL_MODE_REVERTRUN, revertrunMode);
+        MODECASE(DISTTOOL_MODE_PENDINGCOMPONENT, pendingcomponentMode);
+        MODECASE(DISTTOOL_MODE_ADDPROCESSEDCOMPONENT, addprocessedcomponentMode);
+        MODECASE(DISTTOOL_MODE_PROCESSEDCOMPONENT, processedcomponentMode);
+        MODECASE(DISTTOOL_MODE_TOADVANCE, toadvanceMode);
+        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);
+
+    PXOPT_LOOKUP_STR(stage, config->args, "-stage", true, false);
+    PXOPT_LOOKUP_S64(stage_id, config->args, "-stage_id",  true, false);
+    PXOPT_LOOKUP_STR(outroot, config->args, "-outroot", true, false);
+    PXOPT_LOOKUP_BOOL(clean, config->args, "-clean", false);
+    PXOPT_LOOKUP_STR(set_label, config->args, "-set_label", false, false);
+
+    bool require_chip_id = false;
+    if (!strcmp(stage, "raw")) {
+        // need to know the chip id for raw stage so that we can find the masks
+        require_chip_id = true;
+    }
+    PXOPT_LOOKUP_S64(chip_id, config->args, "-chip_id",  require_chip_id, false);
+
+    // TODO: check that stage has an expected value
+    // TODO: check that stage_id actually exists for stage
+    // in magicdstool we queue off of a magic_id so the stage_id, exp_id, and cam_id get looked up 
+    // when the run is queued
+    // Should we also check here that the run is full or cleaned and that all of the images
+    // are magicked. We need to do that at run time as well since the run and magic state can
+    // change between the time that it is queued.
+
+    if (!distRunInsert(config->dbh, 0, stage, stage_id, chip_id, set_label, outroot, clean, "new", 0)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    return true;
+}
+
+static bool definebyqueryMode(pxConfig *config)
+{
+    psError(PS_ERR_PROGRAMMING, true, "-definebyquery is not implemented yet");
+    return false;
+}
+
+static bool updaterunMode(pxConfig *config)
+{
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-dist_id", "dist_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-stage", "stage", "==");;
+    PXOPT_COPY_STR(config->args, where, "-state", "state", "==");
+    PXOPT_COPY_STR(config->args, where, "-label", "label", "==");
+
+    if (!psListLength(where->list)) {
+        psFree(where);
+        psError(PXTOOLS_ERR_DATA, false, "search parameters are required");
+        return false;
+    }
+
+    PXOPT_LOOKUP_STR(state, config->args, "-set_state", false, false);
+    PXOPT_LOOKUP_STR(label, config->args, "-set_label", false, false);
+    PXOPT_LOOKUP_S16(code, config->args, "-code", false, false);
+
+    if ((!state) && (!label) && (!code)) {
+        psError(PXTOOLS_ERR_DATA, false, "parameters (-code or -set_state or -set_label) are required");
+        psFree(where);
+        return false;
+    }
+
+    psString query = psStringCopy("UPDATE distRun");
+
+    if (state) {
+        psStringAppend(&query, " SET state = '%s'", state);
+    }
+
+    if (label) {
+        psStringAppend(&query, " SET label = '%s'", label);
+    }
+
+    if (code) {
+        psStringAppend(&query, " SET fault = %d", code);
+    }
+
+    psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+    psStringAppend(&query, " WHERE %s", whereClause);
+    psFree(whereClause);
+    psFree(where);
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+
+    return true;
+}
+
+static bool revertrunMode(pxConfig *config)
+{
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-dist_id", "distRun.dist_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-stage", "stage", "==");;
+    PXOPT_COPY_S64(config->args, where, "-stage_id", "stage_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-state", "state", "==");
+    PXOPT_COPY_STR(config->args, where, "-label", "label", "==");
+    PXOPT_COPY_S16(config->args, where, "-code", "distComponent.fault", "==");
+
+    // It might be useful to be able to query by the parameters of the underlying runs
+
+    if (!psListLength(where->list) && !psMetadataLookupBool(NULL, config->args, "-all")) {
+        psFree(where);
+        psError(PXTOOLS_ERR_DATA, false, "search parameters are required");
+        return false;
+    }
+
+    if (!psDBTransaction(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(where);
+        return false;
+    }
+
+    // Update state to 'new'
+    int numUpdated;                     // Number updated
+    {
+        psString query = pxDataGet("disttool_revertrun_update.sql");
+        if (!query) {
+            psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
+            return false;
+        }
+
+        if (psListLength(where->list)) {
+            psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+            psStringAppend(&query, " AND %s", whereClause);
+            psFree(whereClause);
+        }
+
+        if (!p_psDBRunQuery(config->dbh, query)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            psFree(query);
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
+            return false;
+        }
+        psFree(query);
+
+        numUpdated = psDBAffectedRows(config->dbh);
+
+#ifdef notdef
+        // don't need this. distRun.state may still be in 'new' state
+        if (numUpdated < 1) {
+            psError(PS_ERR_UNKNOWN, false, "should have affected at least 1 row");
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
+            return false;
+        }
+#endif
+    }
+
+    psLogMsg("disttool", PS_LOG_INFO, "Updated %d dist runs", numUpdated);
+
+    // Delete product
+    int numDeleted;                     // Number deleted
+    {
+        psString query = pxDataGet("disttool_revertrun_delete.sql");
+        if (!query) {
+            psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
+            return false;
+        }
+
+        if (psListLength(where->list)) {
+            psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+            psStringAppend(&query, " AND %s", whereClause);
+            psFree(whereClause);
+        }
+
+        if (!p_psDBRunQuery(config->dbh, query)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            psFree(query);
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
+            return false;
+        }
+        psFree(query);
+
+        numDeleted = psDBAffectedRows(config->dbh);
+    }
+
+    psLogMsg("disttool", PS_LOG_INFO, "Deleted %d distComponents", numDeleted);
+
+    psFree(where);
+
+    if (!psDBCommit(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    return true;
+}
+
+static bool pendingcomponentMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-dist_id", "dist_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-stage", "stage", "==");
+    PXOPT_COPY_STR(config->args, where, "-label", "label", "==");
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    // look for "inputs" that need to processed
+    psString query = pxDataGet("disttool_pendingcomponent.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " WHERE %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        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("disttool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    if (psArrayLength(output)) {
+        // negative simple so the default is true
+        if (!ippdbPrintMetadatas(stdout, output, "pendingcomponent", !simple)) {
+            psError(PS_ERR_UNKNOWN, false, "failed to print array");
+            psFree(output);
+            return false;
+        }
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+static bool addprocessedcomponentMode(pxConfig *config)
+{
+
+    // required values
+    PXOPT_LOOKUP_S64(dist_id, config->args, "-dist_id", true, false);
+    PXOPT_LOOKUP_STR(component, config->args, "-component", true, false);
+
+    // unless fault code is set require filename, bytes, and md5sum
+    bool require_fileinfo = false;
+    PXOPT_LOOKUP_S16(code, config->args, "-code", false, false);
+    if (!code) {
+        require_fileinfo = true;
+    }
+    PXOPT_LOOKUP_S32(bytes, config->args, "-bytes", require_fileinfo, false);
+    PXOPT_LOOKUP_STR(md5sum, config->args, "-md5sum", require_fileinfo, false);
+    PXOPT_LOOKUP_STR(name, config->args, "-name", require_fileinfo, false);
+
+    if (!distComponentInsert(config->dbh, dist_id, component, bytes, md5sum, "full", name, code)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    return true;
+}
+
+static bool toadvanceMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-dist_id", "dist_id", "==");
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    // look for "inputs" that need to processed
+    psString query = pxDataGet("disttool_toadvance.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " WHERE %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        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("disttool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    if (psArrayLength(output)) {
+        // negative simple so the default is true
+        if (!ippdbPrintMetadatas(stdout, output, "toadvance", !simple)) {
+            psError(PS_ERR_UNKNOWN, false, "failed to print array");
+            psFree(output);
+            return false;
+        }
+    }
+
+    psFree(output);
+
+    return true;
+}
+static bool processedcomponentMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-dist_id", "dist_id", "==");
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    // look for "inputs" that need to processed
+    psString query = pxDataGet("disttool_processedcomponent.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " WHERE %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        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("disttool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    if (psArrayLength(output)) {
+        // negative simple so the default is true
+        if (!ippdbPrintMetadatas(stdout, output, "processedcomponent", !simple)) {
+            psError(PS_ERR_UNKNOWN, false, "failed to print array");
+            psFree(output);
+            return false;
+        }
+    }
+
+    psFree(output);
+
+    return true;
+}
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/src/disttool.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/src/disttool.h	(revision 23352)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/src/disttool.h	(revision 23352)
@@ -0,0 +1,39 @@
+/*
+ * disttool.h
+ *
+ * Copyright (C) 2008 IfA
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef DISTTOOL_H
+#define DISTTOOL_H 1
+
+#include "pxtools.h"
+
+typedef enum {
+    DISTTOOL_MODE_NONE      = 0x0,
+    DISTTOOL_MODE_DEFINEBYQUERY,
+    DISTTOOL_MODE_DEFINERUN,
+    DISTTOOL_MODE_UPDATERUN,
+    DISTTOOL_MODE_REVERTRUN,
+    DISTTOOL_MODE_PENDINGCOMPONENT,
+    DISTTOOL_MODE_ADDPROCESSEDCOMPONENT,
+    DISTTOOL_MODE_PROCESSEDCOMPONENT,
+    DISTTOOL_MODE_TOADVANCE,
+} disttoolMode;
+
+pxConfig *disttoolConfig(pxConfig *config, int argc, char **argv);
+
+#endif // DISTTOOL_H
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/src/disttoolConfig.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/src/disttoolConfig.c	(revision 23352)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/src/disttoolConfig.c	(revision 23352)
@@ -0,0 +1,137 @@
+/*
+ * disttoolConfig.c
+ *
+ * Copyright (C) 2008
+ *
+ * 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 "disttool.h"
+
+pxConfig *disttoolConfig(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(PS_ERR_UNKNOWN, false, "Can't find site configuration!\n");
+        psFree(config);
+        return NULL;
+    }
+
+    // -definerun
+    psMetadata *definerunArgs = psMetadataAlloc();
+    psMetadataAddStr(definerunArgs, PS_LIST_TAIL, "-stage",         0, "define stage for bundle (required)", NULL);
+    psMetadataAddS64(definerunArgs, PS_LIST_TAIL, "-stage_id", 0, "define stage_id (required)", 0); 
+    psMetadataAddS64(definerunArgs, PS_LIST_TAIL, "-chip_id", 0, "define chip_id (required for raw stage", 0); 
+    psMetadataAddStr(definerunArgs, PS_LIST_TAIL, "-outroot",  0, "define output destination (required)", NULL);
+    psMetadataAddBool(definerunArgs, PS_LIST_TAIL, "-clean", 0,   "build clean distribution bundle", false);
+    psMetadataAddStr(definerunArgs, PS_LIST_TAIL, "-set_label",    0, "define label for run", NULL);
+    
+    // -updaterun
+    psMetadata *updaterunArgs = psMetadataAlloc();
+    psMetadataAddS64(updaterunArgs, PS_LIST_TAIL, "-dist_id",  0, "define dist_id", 0); 
+    psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-set_state", 0, "new value for state", NULL);
+    psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-set_label", 0, "new value for label", NULL);
+    psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-stage",     0, "value for stage", NULL);
+    psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-state",     0, "value for state", NULL);
+    psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-label",     0, "limit updates to label", NULL);
+    psMetadataAddS16(updaterunArgs, PS_LIST_TAIL, "-code",      0, "define fault code", 0); 
+    
+    // -revertrun
+    psMetadata *revertrunArgs = psMetadataAlloc();
+    psMetadataAddS64(revertrunArgs, PS_LIST_TAIL, "-dist_id", 0, "define dist_id", 0); 
+    psMetadataAddStr(revertrunArgs, PS_LIST_TAIL, "-stage",    0, "define stage", NULL);
+    psMetadataAddS64(revertrunArgs, PS_LIST_TAIL, "-stage_id", 0, "define stage_id", 0); 
+    psMetadataAddStr(revertrunArgs, PS_LIST_TAIL, "-state",    0, "define state", NULL);
+    psMetadataAddStr(revertrunArgs, PS_LIST_TAIL, "-label",    0, "define label", NULL);
+    psMetadataAddS16(revertrunArgs, PS_LIST_TAIL, "-code", 0, "define fault code", 0); 
+    psMetadataAddBool(revertrunArgs, PS_LIST_TAIL, "-all",    0, "revert all faulted runs", NULL);
+    
+    // -pendingcomponent
+    psMetadata *pendingcomponentArgs = psMetadataAlloc();
+    psMetadataAddS64(pendingcomponentArgs, PS_LIST_TAIL, "-dist_id", 0, "define dist_id", 0); 
+    psMetadataAddStr(pendingcomponentArgs, PS_LIST_TAIL, "-stage",    0, "limit results to runs for stage", NULL);
+    psMetadataAddStr(pendingcomponentArgs, PS_LIST_TAIL, "-label",    0, "limit results to label", NULL);
+    psMetadataAddU64(pendingcomponentArgs, PS_LIST_TAIL, "-limit",  0,  "limit result set to N items", 0);
+    psMetadataAddBool(pendingcomponentArgs, PS_LIST_TAIL, "-simple", 0, "use the simple output format", false);
+
+
+    // -addprocessedcomponent
+    psMetadata *addprocessedcomponentArgs = psMetadataAlloc();
+    psMetadataAddS64(addprocessedcomponentArgs, PS_LIST_TAIL, "-dist_id", 0, "define dist_id", 0); 
+    psMetadataAddStr(addprocessedcomponentArgs, PS_LIST_TAIL, "-component", 0, "define component (required)", NULL);
+    psMetadataAddStr(addprocessedcomponentArgs, PS_LIST_TAIL, "-name", 0, "define file name", NULL);
+    psMetadataAddS32(addprocessedcomponentArgs, PS_LIST_TAIL, "-bytes", 0, "define file size", 0); 
+    psMetadataAddStr(addprocessedcomponentArgs, PS_LIST_TAIL, "-md5sum", 0, "define stage for bundle", NULL);
+    psMetadataAddS32(addprocessedcomponentArgs, PS_LIST_TAIL, "-code", 0, "define fault code", 0); 
+
+    // -processedcomponent
+    psMetadata *processedcomponentArgs = psMetadataAlloc();
+    psMetadataAddS64(processedcomponentArgs, PS_LIST_TAIL, "-dist_id", 0, "define dist_id", 0); 
+    psMetadataAddU64(processedcomponentArgs, PS_LIST_TAIL, "-limit",  0,  "limit result set to N items", 0);
+    psMetadataAddBool(processedcomponentArgs, PS_LIST_TAIL, "-simple", 0, "use the simple output format", false);
+
+    // -toadvance
+    psMetadata *toadvanceArgs = psMetadataAlloc();
+    psMetadataAddS64(toadvanceArgs, PS_LIST_TAIL, "-dist_id", 0, "define dist_id", 0); 
+    psMetadataAddU64(toadvanceArgs, PS_LIST_TAIL, "-limit",  0,  "limit result set to N items", 0);
+    psMetadataAddBool(toadvanceArgs, PS_LIST_TAIL, "-simple", 0, "use the simple output format", false);
+
+    psMetadata *argSets = psMetadataAlloc();
+    psMetadata *modes = psMetadataAlloc();
+
+    PXOPT_ADD_MODE("-definerun",    "", DISTTOOL_MODE_DEFINERUN, definerunArgs);
+    PXOPT_ADD_MODE("-updaterun",    "", DISTTOOL_MODE_UPDATERUN, updaterunArgs);
+    PXOPT_ADD_MODE("-revertrun",    "", DISTTOOL_MODE_REVERTRUN, revertrunArgs);
+    PXOPT_ADD_MODE("-pendingcomponent",       "", DISTTOOL_MODE_PENDINGCOMPONENT,    pendingcomponentArgs);
+    PXOPT_ADD_MODE("-addprocessedcomponent",      "", DISTTOOL_MODE_ADDPROCESSEDCOMPONENT, addprocessedcomponentArgs);
+    PXOPT_ADD_MODE("-processedcomponent",      "", DISTTOOL_MODE_PROCESSEDCOMPONENT, processedcomponentArgs);
+    PXOPT_ADD_MODE("-toadvance",      "", DISTTOOL_MODE_TOADVANCE, toadvanceArgs);
+
+    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/cnb_branches/cnb_branch_20090301/ippTools/src/faketool.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/src/faketool.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/src/faketool.c	(revision 23352)
@@ -41,5 +41,5 @@
 static bool revertprocessedimfileMode(pxConfig *config);
 static bool updateprocessedimfileMode(pxConfig *config);
-static bool promoteexpMode(pxConfig *config);
+static bool advanceexpMode(pxConfig *config);
 static bool blockMode(pxConfig *config);
 static bool maskedMode(pxConfig *config);
@@ -52,4 +52,6 @@
 static bool tofullimfileMode(pxConfig *config);
 static bool topurgedimfileMode(pxConfig *config);
+static bool exportrunMode(pxConfig *config);
+static bool importrunMode(pxConfig *config);
 
 # define MODECASE(caseName, func) \
@@ -78,5 +80,5 @@
         MODECASE(FAKETOOL_MODE_REVERTPROCESSEDIMFILE,   revertprocessedimfileMode);
         MODECASE(FAKETOOL_MODE_UPDATEPROCESSEDIMFILE,   updateprocessedimfileMode);
-        MODECASE(FAKETOOL_MODE_PROMOTEEXP,              promoteexpMode);
+        MODECASE(FAKETOOL_MODE_ADVANCEEXP,              advanceexpMode);
         MODECASE(FAKETOOL_MODE_BLOCK,                   blockMode);
         MODECASE(FAKETOOL_MODE_MASKED,                  maskedMode);
@@ -89,4 +91,6 @@
         MODECASE(FAKETOOL_MODE_TOFULLIMFILE,            tofullimfileMode);
         MODECASE(FAKETOOL_MODE_TOPURGEDIMFILE,          topurgedimfileMode);
+        MODECASE(FAKETOOL_MODE_EXPORTRUN,               exportrunMode);
+        MODECASE(FAKETOOL_MODE_IMPORTRUN,               importrunMode);
 
         default:
@@ -1048,5 +1052,5 @@
 }
 
-static bool promoteexpMode(pxConfig *config)
+static bool advanceexpMode(pxConfig *config)
 {
     PS_ASSERT_PTR_NON_NULL(config, false);
@@ -1225,2 +1229,127 @@
     return change_imfile_data_state(config, "purged", "goto_purged");
 }
+
+bool exportrunMode(pxConfig *config)
+{
+  typedef struct ExportTable {
+    char tableName[80];
+    char sqlFilename[80];
+  } ExportTable;
+
+  int numExportTables = 2;
+  
+  PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+  PXOPT_LOOKUP_S64(det_id, config->args, "-fake_id", true,  false);
+  PXOPT_LOOKUP_STR(outfile, config->args, "-outfile", true,  false);
+  PXOPT_LOOKUP_U64(limit,   config->args, "-limit",   false, false);
+
+  FILE *f = fopen (outfile, "w");
+  if (f == NULL) {
+    psError(PS_ERR_UNKNOWN, false, "failed to open output file");
+    return false;
+  }
+
+  psMetadata *where = psMetadataAlloc();
+  PXOPT_COPY_S64(config->args, where, "-fake_id", "fake_id", "==");
+
+  ExportTable tables [] = {
+    {"fakeRun", "faketool_export_run.sql"},
+    {"fakeProcessedImfile", "faketool_export_processed_imfile.sql"},
+  };
+
+  for (int i=0; i < numExportTables; i++) {
+    psString query = pxDataGet(tables[i].sqlFilename);
+    if (!query) {
+      psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+      return false;
+    }
+
+    if (where && psListLength(where->list)) {
+      psString whereClause = psDBGenerateWhereSQL(where, NULL);
+      psStringAppend(&query, " %s", whereClause);
+      psFree(whereClause);
+    }
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+      psString limitString = psDBGenerateLimitSQL(limit);
+      psStringAppend(&query, " %s", limitString);
+      psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+      psError(PS_ERR_UNKNOWN, false, "database error");
+      psFree(query);
+      return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+      psError(PS_ERR_UNKNOWN, false, "database error");
+      return false;
+    }
+    if (!psArrayLength(output)) {
+      psTrace("regtool", PS_LOG_INFO, "no rows found");
+      psFree(output);
+      return true;
+    }
+
+    // we must write the export table in non-simple (true) format
+    if (!ippdbPrintMetadatas(f, output, tables[i].tableName, true)) {
+      psError(PS_ERR_UNKNOWN, false, "failed to print array");
+      psFree(output);
+      return false;
+    }
+    psFree(output);
+  }
+
+    fclose (f);
+
+    return true;
+}
+
+bool importrunMode(pxConfig *config)
+{
+  unsigned int nFail;
+
+  PS_ASSERT_PTR_NON_NULL(config, NULL);
+  
+  PXOPT_LOOKUP_STR(infile, config->args, "-infile", true,  false);
+
+  psMetadata *input = psMetadataConfigRead (NULL, &nFail, infile, false);
+
+  fprintf (stdout, "---- input ----\n");
+  psMetadataPrint (stderr, input, 1);
+
+  psMetadataItem *item = psMetadataLookup (input, "fakeRun");
+  psAssert (item, "entry not in input?");
+  psAssert (item->type == PS_DATA_METADATA_MULTI, "entry not multi?");
+
+  psMetadataItem *entry = psListGet (item->data.list, 0);
+  assert (entry);
+  assert (entry->type == PS_DATA_METADATA);
+  fakeRunRow *fakeRun = fakeRunObjectFromMetadata (entry->data.md);
+  fakeRunInsertObject (config->dbh, fakeRun);
+
+  // fprintf (stdout, "---- fake run ----\n");
+  // psMetadataPrint (stderr, entry->data.md, 1);
+
+  item = psMetadataLookup (input, "fakeProcessedImfile");
+  psAssert (item, "entry not in input?");
+  psAssert (item->type == PS_DATA_METADATA_MULTI, "entry not multi?");
+
+  for (int i = 0; i < item->data.list->n; i++) {
+    psMetadataItem *entry = psListGet (item->data.list, i);
+    assert (entry);
+    assert (entry->type == PS_DATA_METADATA);
+    fakeProcessedImfileRow *fakeProcessedImfile = fakeProcessedImfileObjectFromMetadata (entry->data.md);
+    fakeProcessedImfileInsertObject (config->dbh, fakeProcessedImfile);
+
+    // fprintf (stdout, "---- row %d ----\n", i);
+    // psMetadataPrint (stderr, entry->data.md, 1);
+  }
+
+  return true;
+}
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/src/faketool.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/src/faketool.h	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/src/faketool.h	(revision 23352)
@@ -33,5 +33,5 @@
     FAKETOOL_MODE_REVERTPROCESSEDIMFILE,
     FAKETOOL_MODE_UPDATEPROCESSEDIMFILE,
-    FAKETOOL_MODE_PROMOTEEXP,
+    FAKETOOL_MODE_ADVANCEEXP,
     FAKETOOL_MODE_BLOCK,
     FAKETOOL_MODE_MASKED,
@@ -45,4 +45,6 @@
     FAKETOOL_MODE_TOFULLIMFILE,
     FAKETOOL_MODE_TOPURGEDIMFILE,
+    FAKETOOL_MODE_EXPORTRUN,
+    FAKETOOL_MODE_IMPORTRUN
 } FAKETOOLMode;
 
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/src/faketoolConfig.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/src/faketoolConfig.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/src/faketoolConfig.c	(revision 23352)
@@ -242,9 +242,9 @@
 
 
-    // -promoteexp
-    psMetadata *promoteexpArgs = psMetadataAlloc();
-    psMetadataAddS64(promoteexpArgs, PS_LIST_TAIL, "-fake_id", 0,      "search by fake ID", 0);
-    psMetadataAddStr(promoteexpArgs, PS_LIST_TAIL, "-label",  0,       "search by label ", NULL);
-    psMetadataAddU64(promoteexpArgs, PS_LIST_TAIL, "-limit",  0,       "limit exposures to promote to N items", 0);
+    // -advanceexp
+    psMetadata *advanceexpArgs = psMetadataAlloc();
+    psMetadataAddS64(advanceexpArgs, PS_LIST_TAIL, "-fake_id", 0,      "search by fake ID", 0);
+    psMetadataAddStr(advanceexpArgs, PS_LIST_TAIL, "-label",  0,       "search by label ", NULL);
+    psMetadataAddU64(advanceexpArgs, PS_LIST_TAIL, "-limit",  0,       "limit exposures to advance to N items", 0);
 
     // -block
@@ -301,4 +301,15 @@
     psMetadataAddStr(topurgedimfileArgs, PS_LIST_TAIL, "-class_id",  0,        "class ID to update", NULL);
 
+    // -exportrun
+    psMetadata *exportrunArgs = psMetadataAlloc();
+    psMetadataAddS64(exportrunArgs, PS_LIST_TAIL, "-fake_id", 0,          "export this fake ID (required)", 0);
+    psMetadataAddStr(exportrunArgs, PS_LIST_TAIL, "-outfile", 0,          "export to this file (required)", NULL);
+    psMetadataAddU64(exportrunArgs, PS_LIST_TAIL, "-limit",   0,          "limit result set to N items", 0);
+
+    // -importrun
+    psMetadata *importrunArgs = psMetadataAlloc();
+    psMetadataAddStr(importrunArgs, PS_LIST_TAIL, "-infile",  0,          "import from this file (required)", NULL);
+
+
 
 
@@ -314,5 +325,5 @@
     PXOPT_ADD_MODE("-updateprocessedimfile","change procesed imfile properties",     FAKETOOL_MODE_UPDATEPROCESSEDIMFILE,    updateprocessedimfileArgs);
     PXOPT_ADD_MODE("-revertprocessedimfile", "undo a processed imfile",              FAKETOOL_MODE_REVERTPROCESSEDIMFILE,    revertprocessedimfileArgs);
-    PXOPT_ADD_MODE("-promoteexp",            "promote completed exposoures",         FAKETOOL_MODE_PROMOTEEXP,          promoteexpArgs);
+    PXOPT_ADD_MODE("-advanceexp",            "advance completed exposoures",         FAKETOOL_MODE_ADVANCEEXP,          advanceexpArgs);
     PXOPT_ADD_MODE("-block",                 "set a label block",                    FAKETOOL_MODE_BLOCK,          blockArgs);
     PXOPT_ADD_MODE("-masked",                "show blocked labels",                  FAKETOOL_MODE_MASKED,         maskedArgs);
@@ -325,4 +336,6 @@
     PXOPT_ADD_MODE("-tofullimfile",        "set imfile state to full",               FAKETOOL_MODE_TOFULLIMFILE,         tofullimfileArgs);
     PXOPT_ADD_MODE("-topurgedimfile",      "set imfile state to purged",             FAKETOOL_MODE_TOPURGEDIMFILE,       topurgedimfileArgs);
+    PXOPT_ADD_MODE("-exportrun",            "export run for import on other database", FAKETOOL_MODE_EXPORTRUN, exportrunArgs);
+    PXOPT_ADD_MODE("-importrun",            "import run from metadata file",           FAKETOOL_MODE_IMPORTRUN, importrunArgs);
 
 
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/src/flatcorr.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/src/flatcorr.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/src/flatcorr.c	(revision 23352)
@@ -46,4 +46,6 @@
 static bool inputexpMode(pxConfig *config);
 static bool inputimfileMode(pxConfig *config);
+static bool exportrunMode(pxConfig *config);
+static bool importrunMode(pxConfig *config);
 
 static bool setflatcorrRunState(pxConfig *config, psS64 corr_id, const char *state);
@@ -78,4 +80,6 @@
         MODECASE(FLATCORR_MODE_INPUTEXP,       inputexpMode);
         MODECASE(FLATCORR_MODE_INPUTIMFILE,    inputimfileMode);
+        MODECASE(FLATCORR_MODE_EXPORTRUN,      exportrunMode);
+        MODECASE(FLATCORR_MODE_IMPORTRUN,      importrunMode);
         default:
             psAbort("invalid option (this should not happen)");
@@ -734,2 +738,144 @@
     return true;
 }
+
+bool exportrunMode(pxConfig *config)
+{
+  typedef struct ExportTable {
+    char tableName[80];
+    char sqlFilename[80];
+  } ExportTable;
+  
+  int numExportTables = 3;
+
+  PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+  PXOPT_LOOKUP_S64(det_id, config->args, "-corr_id", true,  false);
+  PXOPT_LOOKUP_STR(outfile, config->args, "-outfile", true,  false);
+  PXOPT_LOOKUP_U64(limit,   config->args, "-limit",   false, false);
+
+  FILE *f = fopen (outfile, "w");
+  if (f == NULL) {
+    psError(PS_ERR_UNKNOWN, false, "failed to open output file");
+    return false;
+  }
+
+  psMetadata *where = psMetadataAlloc();
+  PXOPT_COPY_S64(config->args, where, "-coor_id", "corr_id", "==");
+
+  ExportTable tables [] = {
+    {"flatcorrRun", "flatcorrtool_export_run.sql"},
+    {"flatcorrCamLink", "flatcorrtool_export_cam_link.sql"},
+    {"flatcorrChipLink", "flatcorr_export_chip_link.sql"},
+  };
+
+  for (int i=0; i < numExportTables; i++) {
+    psString query = pxDataGet(tables[i].sqlFilename);
+    if (!query) {
+      psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+      return false;
+    }
+
+    if (where && psListLength(where->list)) {
+      psString whereClause = psDBGenerateWhereSQL(where, NULL);
+      psStringAppend(&query, " %s", whereClause);
+      psFree(whereClause);
+    }
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+      psString limitString = psDBGenerateLimitSQL(limit);
+      psStringAppend(&query, " %s", limitString);
+      psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+      psError(PS_ERR_UNKNOWN, false, "database error");
+      psFree(query);
+      return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+      psError(PS_ERR_UNKNOWN, false, "database error");
+      return false;
+    }
+    if (!psArrayLength(output)) {
+      psTrace("regtool", PS_LOG_INFO, "no rows found");
+      psFree(output);
+      return true;
+    }
+
+    // we must write the export table in non-simple (true) format
+    if (!ippdbPrintMetadatas(f, output, tables[i].tableName, true)) {
+      psError(PS_ERR_UNKNOWN, false, "failed to print array");
+      psFree(output);
+      return false;
+    }
+    psFree(output);
+  }
+
+    fclose (f);
+
+    return true;
+}
+
+bool importrunMode(pxConfig *config)
+{
+  unsigned int nFail;
+  psMetadataItem *item, *entry;
+  
+  int numImportTables = 3;
+  
+  char tables[3] [80] = {"flatcorrRun", "flatcorrCamLink", "flatcorrChipLink"};
+
+  PS_ASSERT_PTR_NON_NULL(config, NULL);
+  
+  PXOPT_LOOKUP_STR(infile, config->args, "-infile", true,  false);
+
+  psMetadata *input = psMetadataConfigRead (NULL, &nFail, infile, false);
+
+  fprintf (stdout, "---- input ----\n");
+  psMetadataPrint (stderr, input, 1);
+
+  for (int i = 0; i < numImportTables; i++) {
+    item = psMetadataLookup (input, tables[i]);
+    psAssert (item, "entry not in input?");
+    psAssert (item->type == PS_DATA_METADATA_MULTI, "entry not multi?");
+    
+    entry = psListGet (item->data.list, 0);
+    assert (entry);
+    assert (entry->type == PS_DATA_METADATA);
+  
+    switch (i) {
+      case 0:
+      {
+        flatcorrRunRow *flatcorrRun = flatcorrRunObjectFromMetadata (entry->data.md);
+        flatcorrRunInsertObject (config->dbh, flatcorrRun);
+
+        // fprintf (stdout, "---- flatcorr run ----\n");
+        // psMetadataPrint (stderr, entry->data.md, 1);
+        break;
+      } 
+      case 1:
+      {
+        flatcorrCamLinkRow *flatcorrCamLink = flatcorrCamLinkObjectFromMetadata (entry->data.md);
+        flatcorrCamLinkInsertObject (config->dbh, flatcorrCamLink);
+
+        // fprintf (stdout, "---- flatcorr cam link ----\n");
+        // psMetadataPrint (stderr, entry->data.md, 1);
+        break;
+      }
+      case 2:
+      {
+        flatcorrChipLinkRow *flatcorrChipLink = flatcorrChipLinkObjectFromMetadata (entry->data.md);
+        flatcorrChipLinkInsertObject (config->dbh, flatcorrChipLink);
+
+        // fprintf (stdout, "---- flatcorr chip link ----\n");
+        // psMetadataPrint (stderr, entry->data.md, 1);
+        break;
+      }
+    }
+  }
+  return true;
+}
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/src/flatcorr.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/src/flatcorr.h	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/src/flatcorr.h	(revision 23352)
@@ -35,5 +35,7 @@
     FLATCORR_MODE_UPDATERUN,
     FLATCORR_MODE_INPUTEXP,
-    FLATCORR_MODE_INPUTIMFILE
+    FLATCORR_MODE_INPUTIMFILE,
+    FLATCORR_MODE_EXPORTRUN,
+    FLATCORR_MODE_IMPORTRUN
 } flatcorrMode;
 
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/src/flatcorrConfig.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/src/flatcorrConfig.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/src/flatcorrConfig.c	(revision 23352)
@@ -125,4 +125,15 @@
     psMetadataAddU64(inputimfileArgs, PS_LIST_TAIL, "-limit",   0, "limit result set to N items", 0);
 
+    // -exportrun
+    psMetadata *exportrunArgs = psMetadataAlloc();
+    psMetadataAddS64(exportrunArgs, PS_LIST_TAIL, "-corr_id", 0,          "export this correction ID (required)", 0);
+    psMetadataAddStr(exportrunArgs, PS_LIST_TAIL, "-outfile", 0,          "export to this file (required)", NULL);
+    psMetadataAddU64(exportrunArgs, PS_LIST_TAIL, "-limit",   0,          "limit result set to N items", 0);
+
+    // -importrun
+    psMetadata *importrunArgs = psMetadataAlloc();
+    psMetadataAddStr(importrunArgs, PS_LIST_TAIL, "-infile",  0,          "import from this file (required)", NULL);
+
+
     psFree(now);
 
@@ -141,4 +152,6 @@
     PXOPT_ADD_MODE("-inputexp",       "list exposures for a correction run",               FLATCORR_MODE_INPUTEXP,       inputexpArgs);
     PXOPT_ADD_MODE("-inputimfile",    "list imfiles for a chip run",                       FLATCORR_MODE_INPUTIMFILE,    inputimfileArgs);
+    PXOPT_ADD_MODE("-exportrun",            "export run for import on other database", FLATCORR_MODE_EXPORTRUN, exportrunArgs);
+    PXOPT_ADD_MODE("-importrun",            "import run from metadata file",           FLATCORR_MODE_IMPORTRUN, importrunArgs);
 
     if (!pxGetOptions(stderr, argc, argv, config, modes, argSets)) {
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/src/pxadmin.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/src/pxadmin.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/src/pxadmin.c	(revision 23352)
@@ -29,4 +29,5 @@
 
 bool createMode(pxConfig *config);
+bool createMirrorMode(pxConfig *config);
 bool deleteMode(pxConfig *config);
 static bool runMultipleStatments(pxConfig *config, const char *query);
@@ -53,4 +54,9 @@
             }
             break;
+        case PXADMIN_MODE_CREATE_MIRROR:
+            if (!createMirrorMode(config)) {
+                goto FAIL;
+            }
+            break;
         case PXADMIN_MODE_DELETE:
             if (!deleteMode(config)) {
@@ -86,4 +92,40 @@
 
     psString query = pxDataGet("pxadmin_create_tables.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    // BEGIN
+    if (!psDBTransaction(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    if (!runMultipleStatments(config, query)) {
+        if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+        }
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    // COMMIT
+    if (!psDBCommit(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    return true;
+}
+
+
+bool createMirrorMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psString query = pxDataGet("pxadmin_create_mirror_tables.sql");
     if (!query) {
         psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/src/pxadmin.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/src/pxadmin.h	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/src/pxadmin.h	(revision 23352)
@@ -26,4 +26,5 @@
     PXADMIN_MODE_NONE      = 0x0,
     PXADMIN_MODE_CREATE,
+    PXADMIN_MODE_CREATE_MIRROR,
     PXADMIN_MODE_DELETE,
     PXADMIN_MODE_RECREATE
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/src/pxadminConfig.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/src/pxadminConfig.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/src/pxadminConfig.c	(revision 23352)
@@ -31,5 +31,5 @@
     fprintf (stderr, "\nPan-STARRS DataBase Admin Tool\n\n");
     fprintf (stderr, "Usage: %s [mode]\n", program);
-    fprintf (stderr, " [mode] : -create | -delete\n\n");
+    fprintf (stderr, " [mode] : -create | -create-mirror | -delete\n\n");
 
     psMetadataItem *server = pmConfigUserSite(config->modules, "DBSERVER",   PS_DATA_STRING);
@@ -87,4 +87,11 @@
         config->mode = PXADMIN_MODE_CREATE;
     }
+    if ((N = psArgumentGet(argc, argv, "-create-mirror"))) {
+        psArgumentRemove(N, &argc, argv);
+        if (config->mode) {
+            psAbort("only one mode selection is allowed");
+        }
+        config->mode = PXADMIN_MODE_CREATE_MIRROR;
+    }
     if ((N = psArgumentGet(argc, argv, "-delete"))) {
         psArgumentRemove(N, &argc, argv);
@@ -105,4 +112,6 @@
     psMetadataAddStr(arguments, PS_LIST_TAIL, "-create", 0,
             "create all IPP tables", "");
+    psMetadataAddStr(arguments, PS_LIST_TAIL, "-create-mirror", 0,
+            "mirror all IPP tables", "");
     psMetadataAddStr(arguments, PS_LIST_TAIL, "-delete", 0,
             "delete all IPP tables", "");
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/src/pxtools.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/src/pxtools.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/src/pxtools.c	(revision 23352)
@@ -30,6 +30,4 @@
     PS_ASSERT_PTR_NON_NULL(state, false);
     
-    // XXX replace strncmp with strcmp
-
     if (!strcmp(state, "new")) return true;
     if (!strcmp(state, "reg")) return true;
@@ -38,9 +36,16 @@
     if (!strcmp(state, "wait")) return true;
     if (!strcmp(state, "goto_cleaned")) return true;
+    if (!strcmp(state, "error_cleaned")) return true;
+    if (!strcmp(state, "goto_scrubbed")) return true;
+    if (!strcmp(state, "error_scrubbed")) return true;
     if (!strcmp(state, "cleaned")) return true;
     if (!strcmp(state, "update")) return true;
     if (!strcmp(state, "purged")) return true;
     if (!strcmp(state, "goto_purged")) return true;
+    if (!strcmp(state, "error_purged")) return true;
 
     return false;
 }
+
+// 'scrubbed' is a virtual state equivalent to cleaned, but allows files to be removed
+// even if the config files is missing
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/src/pxwarp.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/src/pxwarp.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/src/pxwarp.c	(revision 23352)
@@ -58,5 +58,5 @@
     // check that state is a valid string value
     if (!pxIsValidState(state)) {
-        psError(PS_ERR_UNKNOWN, false, "invalid chipRun state: %s", state);
+        psError(PS_ERR_UNKNOWN, false, "invalid warpRun state: %s", state);
         return false;
     }
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/src/regtool.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/src/regtool.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/src/regtool.c	(revision 23352)
@@ -44,4 +44,6 @@
 static bool updateprocessedexpMode(pxConfig *config);
 static bool cleardupexpMode(pxConfig *config);
+static bool exportrunMode(pxConfig *config);
+static bool importrunMode(pxConfig *config);
 
 # define MODECASE(caseName, func) \
@@ -76,4 +78,6 @@
         MODECASE(REGTOOL_MODE_UPDATEPROCESSEDEXP,    updateprocessedexpMode);
         MODECASE(REGTOOL_MODE_CLEARDUPEXP,           cleardupexpMode);
+        MODECASE(CHIPTOOL_MODE_EXPORTRUN,            exportrunMode);
+        MODECASE(CHIPTOOL_MODE_IMPORTRUN,            importrunMode);
         default:
             psAbort("invalid option (this should not happen)");
@@ -1107,2 +1111,128 @@
     return true;
 }
+
+bool exportrunMode(pxConfig *config)
+{
+  typedef struct ExportTable {
+    char tableName[80];
+    char sqlFilename[80];
+  } ExportTable;
+  
+  int numExportTables = 2;
+
+  PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+  PXOPT_LOOKUP_S64(det_id, config->args, "-exp_id", true,  false);
+  PXOPT_LOOKUP_STR(outfile, config->args, "-outfile", true,  false);
+  PXOPT_LOOKUP_U64(limit,   config->args, "-limit",   false, false);
+
+  FILE *f = fopen (outfile, "w");
+  if (f == NULL) {
+    psError(PS_ERR_UNKNOWN, false, "failed to open output file");
+    return false;
+  }
+
+  psMetadata *where = psMetadataAlloc();
+  PXOPT_COPY_S64(config->args, where, "-exp_id", "exp_id", "==");
+
+  ExportTable tables [] = {
+    {"rawExp", "regtool_export_raw__exp.sql"},
+    {"rawImfile", "regtool_export_raw_imfile.sql"},
+  };
+
+  for (int i=0; i < numExportTables; i++) {
+    psString query = pxDataGet(tables[i].sqlFilename);
+    if (!query) {
+      psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+      return false;
+    }
+
+    if (where && psListLength(where->list)) {
+      psString whereClause = psDBGenerateWhereSQL(where, NULL);
+      psStringAppend(&query, " %s", whereClause);
+      psFree(whereClause);
+    }
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+      psString limitString = psDBGenerateLimitSQL(limit);
+      psStringAppend(&query, " %s", limitString);
+      psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+      psError(PS_ERR_UNKNOWN, false, "database error");
+      psFree(query);
+      return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+      psError(PS_ERR_UNKNOWN, false, "database error");
+      return false;
+    }
+    if (!psArrayLength(output)) {
+      psTrace("regtool", PS_LOG_INFO, "no rows found");
+      psFree(output);
+      return true;
+    }
+
+    // we must write the export table in non-simple (true) format
+    if (!ippdbPrintMetadatas(f, output, tables[i].tableName, true)) {
+      psError(PS_ERR_UNKNOWN, false, "failed to print array");
+      psFree(output);
+      return false;
+    }
+    psFree(output);
+  }
+
+  fclose (f);
+
+  return true;
+}
+
+bool importrunMode(pxConfig *config)
+{
+  unsigned int nFail;
+
+  PS_ASSERT_PTR_NON_NULL(config, NULL);
+  
+  PXOPT_LOOKUP_STR(infile, config->args, "-infile", true,  false);
+
+  psMetadata *input = psMetadataConfigRead (NULL, &nFail, infile, false);
+
+  fprintf (stdout, "---- input ----\n");
+  psMetadataPrint (stderr, input, 1);
+
+  psMetadataItem *item = psMetadataLookup (input, "rawExp");
+  psAssert (item, "entry not in input?");
+  psAssert (item->type == PS_DATA_METADATA_MULTI, "entry not multi?");
+  
+  psMetadataItem *entry = psListGet (item->data.list, 0);
+  assert (entry);
+  assert (entry->type == PS_DATA_METADATA);
+  rawExpRow *rawExp = rawExpObjectFromMetadata (entry->data.md);
+  rawExpInsertObject (config->dbh, rawExp);
+
+  // fprintf (stdout, "---- raw exp ----\n");
+  // psMetadataPrint (stderr, entry->data.md, 1);
+
+  item = psMetadataLookup (input, "rawImfile");
+  psAssert (item, "entry not in input?");
+  psAssert (item->type == PS_DATA_METADATA_MULTI, "entry not multi?");
+
+  for (int i = 0; i < item->data.list->n; i++) {
+    psMetadataItem *entry = psListGet (item->data.list, i);
+    assert (entry);
+    assert (entry->type == PS_DATA_METADATA);
+    rawImfileRow *rawImfile = rawImfileObjectFromMetadata (entry->data.md);
+    rawImfileInsertObject (config->dbh, rawImfile);
+
+    // fprintf (stdout, "---- row %d ----\n", i);
+    // psMetadataPrint (stderr, entry->data.md, 1);
+  }
+
+  return true;
+}
+
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/src/regtool.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/src/regtool.h	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/src/regtool.h	(revision 23352)
@@ -36,4 +36,6 @@
     REGTOOL_MODE_UPDATEPROCESSEDEXP,
     REGTOOL_MODE_CLEARDUPEXP,
+    REGTOOL_MODE_EXPORTRUN,
+    REGTOOL_MODE_IMPORTRUN
 } regtoolMode;
 
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/src/regtoolConfig.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/src/regtoolConfig.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/src/regtoolConfig.c	(revision 23352)
@@ -278,4 +278,15 @@
     psMetadataAddS16(updatedprocessedexpArgs, PS_LIST_TAIL, "-code",    0,            "set fault code (required)", INT16_MAX);
 
+    // -exportrun
+    psMetadata *exportrunArgs = psMetadataAlloc();
+    psMetadataAddS64(exportrunArgs, PS_LIST_TAIL, "-exp_id", 0,          "export this exposure ID (required)", 0);
+    psMetadataAddStr(exportrunArgs, PS_LIST_TAIL, "-outfile", 0,          "export to this file (required)", NULL);
+    psMetadataAddU64(exportrunArgs, PS_LIST_TAIL, "-limit",   0,          "limit result set to N items", 0);
+
+    // -importrun
+    psMetadata *importrunArgs = psMetadataAlloc();
+    psMetadataAddStr(importrunArgs, PS_LIST_TAIL, "-infile",  0,          "import from this file (required)", NULL);
+
+
     // -cleardupexp
     psMetadata *cleardupexpArgs = psMetadataAlloc();
@@ -295,4 +306,6 @@
     PXOPT_ADD_MODE("-updateprocessedexp",      "", REGTOOL_MODE_UPDATEPROCESSEDEXP,      updatedprocessedexpArgs);
     PXOPT_ADD_MODE("-cleardupexp",             "", REGTOOL_MODE_CLEARDUPEXP,      cleardupexpArgs);
+    PXOPT_ADD_MODE("-exportrun",            "export run for import on other database", REGTOOL_MODE_EXPORTRUN, exportrunArgs);
+    PXOPT_ADD_MODE("-importrun",            "import run from metadata file",           REGTOOL_MODE_IMPORTRUN, importrunArgs);
 
     if (!pxGetOptions(stderr, argc, argv, config, modes, argSets)) {
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/src/stacktool.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/src/stacktool.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/src/stacktool.c	(revision 23352)
@@ -44,4 +44,6 @@
 static bool donecleanupMode(pxConfig *config);
 static bool updatesumskyfileMode(pxConfig *config);
+static bool exportrunMode(pxConfig *config);
+static bool importrunMode(pxConfig *config);
 
 static bool setstackRunState(pxConfig *config, psS64 stack_id, const char *state);
@@ -78,4 +80,6 @@
         MODECASE(STACKTOOL_MODE_DONECLEANUP,           donecleanupMode);
         MODECASE(STACKTOOL_MODE_UPDATESUMSKYFILE,      updatesumskyfileMode);
+        MODECASE(STACKTOOL_MODE_EXPORTRUN,             exportrunMode);
+        MODECASE(STACKTOOL_MODE_IMPORTRUN,             importrunMode);
         default:
             psAbort("invalid option (this should not happen)");
@@ -1180,2 +1184,143 @@
 }
 
+bool exportrunMode(pxConfig *config)
+{
+  typedef struct ExportTable {
+    char tableName[80];
+    char sqlFilename[80];
+  } ExportTable;
+
+  int numExportTables = 3;
+  
+  PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+  PXOPT_LOOKUP_S64(det_id, config->args, "-stack_id", true,  false);
+  PXOPT_LOOKUP_STR(outfile, config->args, "-outfile", true,  false);
+  PXOPT_LOOKUP_U64(limit,   config->args, "-limit",   false, false);
+
+  FILE *f = fopen (outfile, "w");
+  if (f == NULL) {
+      psError(PS_ERR_UNKNOWN, false, "failed to open output file");
+      return false;
+  }
+
+  psMetadata *where = psMetadataAlloc();
+  PXOPT_COPY_S64(config->args, where, "-stack_id", "stack_id", "==");
+
+  ExportTable tables [] = {
+    {"stackRun", "stacktool_export_run.sql"},
+    {"stackInputSkyfile", "stacktool_export_input_skyfile.sql"},
+    {"stackSumSkyfile", "stacktool_export_sum_skyfile.sql"},
+  };
+
+  for (int i=0; i < numExportTables; i++) {
+    psString query = pxDataGet(tables[i].sqlFilename);
+    if (!query) {
+      psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+      return false;
+    }
+
+    if (where && psListLength(where->list)) {
+      psString whereClause = psDBGenerateWhereSQL(where, NULL);
+      psStringAppend(&query, " %s", whereClause);
+      psFree(whereClause);
+    }
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+      psString limitString = psDBGenerateLimitSQL(limit);
+      psStringAppend(&query, " %s", limitString);
+      psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+      psError(PS_ERR_UNKNOWN, false, "database error");
+      psFree(query);
+      return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+      psError(PS_ERR_UNKNOWN, false, "database error");
+      return false;
+    }
+    if (!psArrayLength(output)) {
+      psTrace("regtool", PS_LOG_INFO, "no rows found");
+      psFree(output);
+      return true;
+    }
+    psFree(output);
+  }
+
+    fclose (f);
+
+    return true;
+}
+
+bool importrunMode(pxConfig *config)
+{
+  unsigned int nFail;
+  
+  int numImportTables = 2;
+  
+  char tables[2] [80] = {"stackInputSkyfile", "stackSumSkyfile"};
+
+  PS_ASSERT_PTR_NON_NULL(config, NULL);
+  
+  PXOPT_LOOKUP_STR(infile, config->args, "-infile", true,  false);
+
+  psMetadata *input = psMetadataConfigRead (NULL, &nFail, infile, false);
+
+  fprintf (stdout, "---- input ----\n");
+  psMetadataPrint (stderr, input, 1);
+
+  psMetadataItem *item = psMetadataLookup (input, "stackRun");
+  psAssert (item, "entry not in input?");
+  psAssert (item->type == PS_DATA_METADATA_MULTI, "entry not multi?");
+
+  psMetadataItem *entry = psListGet (item->data.list, 0);
+  assert (entry);
+  assert (entry->type == PS_DATA_METADATA);
+  stackRunRow *stackRun = stackRunObjectFromMetadata (entry->data.md);
+  stackRunInsertObject (config->dbh, stackRun);
+
+  // fprintf (stdout, "---- stack run ----\n");
+  // psMetadataPrint (stderr, entry->data.md, 1);
+
+  for (int i = 0; i < numImportTables; i++) {
+    psMetadataItem *item = psMetadataLookup (input, tables[i]);
+    psAssert (item, "entry not in input?");
+    psAssert (item->type == PS_DATA_METADATA_MULTI, "entry not multi?");
+  
+    switch (i) {
+      case 0:
+        for (int i = 0; i < item->data.list->n; i++) {
+          entry = psListGet (item->data.list, i);
+          assert (entry);
+          assert (entry->type == PS_DATA_METADATA);
+          stackInputSkyfileRow *stackInputSkyfile = stackInputSkyfileObjectFromMetadata (entry->data.md);
+          stackInputSkyfileInsertObject (config->dbh, stackInputSkyfile);
+
+          // fprintf (stdout, "---- row %d ----\n", i);
+          // psMetadataPrint (stderr, entry->data.md, 1);
+        }
+        break;
+        
+      case 1:
+        for (int i = 0; i < item->data.list->n; i++) {
+          entry = psListGet (item->data.list, i);
+          assert (entry);
+          assert (entry->type == PS_DATA_METADATA);
+          stackSumSkyfileRow *stackSumSkyfile = stackSumSkyfileObjectFromMetadata (entry->data.md);
+          stackSumSkyfileInsertObject (config->dbh, stackSumSkyfile);
+
+          // fprintf (stdout, "---- row %d ----\n", i);
+          // psMetadataPrint (stderr, entry->data.md, 1);
+        }
+        break;
+    }
+  }
+  
+  return true;
+}
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/src/stacktool.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/src/stacktool.h	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/src/stacktool.h	(revision 23352)
@@ -38,4 +38,6 @@
     STACKTOOL_MODE_DONECLEANUP,
     STACKTOOL_MODE_UPDATESUMSKYFILE,
+    STACKTOOL_MODE_EXPORTRUN,
+    STACKTOOL_MODE_IMPORTRUN
 } stacktoolMode;
 
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/src/stacktoolConfig.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/src/stacktoolConfig.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/src/stacktoolConfig.c	(revision 23352)
@@ -187,4 +187,15 @@
     psMetadataAddS16(updatesumskyfileArgs, PS_LIST_TAIL, "-code", 0,            "set fault code (required)", 0);
 
+    // -exportrun
+    psMetadata *exportrunArgs = psMetadataAlloc();
+    psMetadataAddS64(exportrunArgs, PS_LIST_TAIL, "-stack_id", 0,          "export this stack ID (required)", 0);
+    psMetadataAddStr(exportrunArgs, PS_LIST_TAIL, "-outfile", 0,          "export to this file (required)", NULL);
+    psMetadataAddU64(exportrunArgs, PS_LIST_TAIL, "-limit",   0,          "limit result set to N items", 0);
+
+    // -importrun
+    psMetadata *importrunArgs = psMetadataAlloc();
+    psMetadataAddStr(importrunArgs, PS_LIST_TAIL, "-infile",  0,          "import from this file (required)", NULL);
+
+
     psFree(now);
 
@@ -205,4 +216,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("-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);
 
     if (!pxGetOptions(stderr, argc, argv, config, modes, argSets)) {
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/src/warptool.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/src/warptool.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/src/warptool.c	(revision 23352)
@@ -54,4 +54,6 @@
 static bool tofullskyfileMode(pxConfig *config);
 static bool updateskyfileMode(pxConfig *config);
+static bool exportrunMode(pxConfig *config);
+static bool importrunMode(pxConfig *config);
 
 static bool parseAndInsertSkyCellMap(pxConfig *config, const char *mapfile);
@@ -99,4 +101,6 @@
         MODECASE(WARPTOOL_MODE_TOFULLSKYFILE,      tofullskyfileMode);
         MODECASE(WARPTOOL_MODE_UPDATESKYFILE,      updateskyfileMode);
+        MODECASE(WARPTOOL_MODE_EXPORTRUN,          exportrunMode);
+        MODECASE(WARPTOOL_MODE_IMPORTRUN,          importrunMode);
 
         default:
@@ -1680,2 +1684,163 @@
     return true;
 }
+
+bool exportrunMode(pxConfig *config)
+{
+  typedef struct ExportTable {
+    char tableName[80];
+    char sqlFilename[80];
+  } ExportTable;
+
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    PXOPT_LOOKUP_S64(det_id, config->args, "-warp_id", true,  false);
+    PXOPT_LOOKUP_STR(outfile, config->args, "-outfile", true,  false);
+    PXOPT_LOOKUP_U64(limit,   config->args, "-limit",   false, false);
+
+    FILE *f = fopen (outfile, "w");
+    if (f == NULL) {
+        psError(PS_ERR_UNKNOWN, false, "failed to open output file");
+        return false;
+    }
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-warp_id", "warp_id", "==");
+
+    ExportTable tables [] = {
+      {"warpRun", "warptool_export_run.sql"},
+      {"warpImfile", "warptool_export_imfile.sql"},
+      {"warpSkyfile", "warptool_export_skyfile.sql"},
+      {"warpSkyCellMap", "warptool_export_sky_cell_map.sql"},
+    };
+
+
+    for (int i=0; i < sizeof(tables); i++) {
+      psString query = pxDataGet(tables[i].sqlFilename);
+      if (!query) {
+          psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+          return false;
+      }
+
+      if (where && psListLength(where->list)) {
+          psString whereClause = psDBGenerateWhereSQL(where, NULL);
+          psStringAppend(&query, " %s", whereClause);
+          psFree(whereClause);
+      }
+
+      // treat limit == 0 as "no limit"
+      if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+      }
+
+      if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+      }
+      psFree(query);
+
+      psArray *output = p_psDBFetchResult(config->dbh);
+      if (!output) {
+          psError(PS_ERR_UNKNOWN, false, "database error");
+          return false;
+      }
+      if (!psArrayLength(output)) {
+        psTrace("regtool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+      }
+
+      // we must write the export table in non-simple (true) format
+      if (!ippdbPrintMetadatas(f, output, tables[i].tableName, true)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+      }
+      psFree(output);
+    }
+
+    fclose (f);
+
+    return true;
+}
+
+bool importrunMode(pxConfig *config)
+{
+  unsigned int nFail;
+
+  int numImportTables = 3;
+  
+  char tables[3] [80] = {"warpImfile", "warpSkyfile", "warpSkyCellMap"};
+
+  PS_ASSERT_PTR_NON_NULL(config, NULL);
+  
+  PXOPT_LOOKUP_STR(infile, config->args, "-infile", true,  false);
+
+  psMetadata *input = psMetadataConfigRead (NULL, &nFail, infile, false);
+
+  fprintf (stdout, "---- input ----\n");
+  psMetadataPrint (stderr, input, 1);
+
+  psMetadataItem *item = psMetadataLookup (input, "warpRun");
+  psAssert (item, "entry not in input?");
+  psAssert (item->type == PS_DATA_METADATA_MULTI, "entry not multi?");
+  
+  psMetadataItem *entry = psListGet (item->data.list, 0);
+  assert (entry);
+  assert (entry->type == PS_DATA_METADATA);
+  warpRunRow *warpRun = warpRunObjectFromMetadata (entry->data.md);
+  warpRunInsertObject (config->dbh, warpRun);
+
+  // fprintf (stdout, "---- warp run ----\n");
+  // psMetadataPrint (stderr, entry->data.md, 1);
+
+  for (int i = 0; i < numImportTables; i++) {
+    item = psMetadataLookup (input, tables[i]);
+    psAssert (item, "entry not in input?");
+    psAssert (item->type == PS_DATA_METADATA_MULTI, "entry not multi?");
+    
+    switch (i) {
+      case 0:  
+        for (int i = 0; i < item->data.list->n; i++) {
+          entry = psListGet (item->data.list, i);
+          assert (entry);
+          assert (entry->type == PS_DATA_METADATA);
+          warpImfileRow *warpImfile = warpImfileObjectFromMetadata (entry->data.md);
+          warpImfileInsertObject (config->dbh, warpImfile);
+
+          // fprintf (stdout, "---- row %d ----\n", i);
+          // psMetadataPrint (stderr, entry->data.md, 1);
+        }
+        break;
+        
+      case 1:  
+        for (int i = 0; i < item->data.list->n; i++) {
+          entry = psListGet (item->data.list, i);
+          assert (entry);
+          assert (entry->type == PS_DATA_METADATA);
+          warpSkyfileRow *warpSkyfile = warpSkyfileObjectFromMetadata (entry->data.md);
+          warpSkyfileInsertObject (config->dbh, warpSkyfile);
+
+          // fprintf (stdout, "---- row %d ----\n", i);
+          // psMetadataPrint (stderr, entry->data.md, 1);
+        }
+        break;
+        
+      case 2:  
+        for (int i = 0; i < item->data.list->n; i++) {
+          entry = psListGet (item->data.list, i);
+          assert (entry);
+          assert (entry->type == PS_DATA_METADATA);
+          warpSkyCellMapRow *warpSkyCellMap = warpSkyCellMapObjectFromMetadata (entry->data.md);
+          warpSkyCellMapInsertObject (config->dbh, warpSkyCellMap);
+
+          // fprintf (stdout, "---- row %d ----\n", i);
+          // psMetadataPrint (stderr, entry->data.md, 1);
+        }
+        break;
+    }
+  }
+  return true;
+}
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/src/warptool.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/src/warptool.h	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/src/warptool.h	(revision 23352)
@@ -49,4 +49,6 @@
     WARPTOOL_MODE_TOFULLSKYFILE,
     WARPTOOL_MODE_UPDATESKYFILE,
+    WARPTOOL_MODE_EXPORTRUN,
+    WARPTOOL_MODE_IMPORTRUN
 } warptoolMode;
 
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/src/warptoolConfig.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/src/warptoolConfig.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/src/warptoolConfig.c	(revision 23352)
@@ -354,4 +354,15 @@
     psMetadataAddS16(updateskyfileArgs, PS_LIST_TAIL, "-code",  0,      "new fault code", 0);
 
+    // -exportrun
+    psMetadata *exportrunArgs = psMetadataAlloc();
+    psMetadataAddS64(exportrunArgs, PS_LIST_TAIL, "-warp_id", 0,          "export this warp ID (required)", 0);
+    psMetadataAddStr(exportrunArgs, PS_LIST_TAIL, "-outfile", 0,          "export to this file (required)", NULL);
+    psMetadataAddU64(exportrunArgs, PS_LIST_TAIL, "-limit",   0,          "limit result set to N items", 0);
+
+    // -importrun
+    psMetadata *importrunArgs = psMetadataAlloc();
+    psMetadataAddStr(importrunArgs, PS_LIST_TAIL, "-infile",  0,          "import from this file (required)", NULL);
+
+
     psFree(now);
     psMetadata *argSets = psMetadataAlloc();
@@ -380,4 +391,6 @@
     PXOPT_ADD_MODE("-tofullskyfile", "set skyfile as full (updated)", WARPTOOL_MODE_TOFULLSKYFILE, tofullskyfileArgs);
     PXOPT_ADD_MODE("-updateskyfile", "update fault code for skyfile", WARPTOOL_MODE_UPDATESKYFILE, updateskyfileArgs);
+    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);
 
     if (!pxGetOptions(stderr, argc, argv, config, modes, argSets)) {
Index: /branches/cnb_branches/cnb_branch_20090301/ippconfig/dvo.ps1v1.photcodes
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippconfig/dvo.ps1v1.photcodes	(revision 23352)
+++ /branches/cnb_branches/cnb_branch_20090301/ippconfig/dvo.ps1v1.photcodes	(revision 23352)
@@ -0,0 +1,906 @@
+
+# this file defines the layout for the PS1 working science database.  it has
+# average photcodes of grizy for PS1 and JHK for 2MASS and UKIDSS references
+
+# this file contains the list of valid photometry codes.  there should
+# be one code for each combination of CCD, filter, and telescope (and
+# possibly additional ones if filter transmission reduces with time).
+# The code on each line is a unique number, while the name is a
+# representative name.  The type defines how the photometry is
+# treated.  There can only be one primary photcode.  There should only
+# be a few secondary photcodes: these are the calibrated values for
+# stars.  The dependent photcodes are those which are equivalent to a
+# primary or secondary photcode, and are equated after photometric
+# solutions.  Reference photcodes are for anything without a dynamic
+# solution: the reference values are provided externally.
+
+# NOTE: the photcodes defined below supplement CFHT Elixir values.
+# Do not use this photcode file with old CFHT DVO databases.
+
+# camera ranges used in this file:
+# megacam:  u: 100-139, g: 200-239, r: 300-339, i: 400-439, z: 500-539
+# cfh12k:   B: 140-152, V: 240-252, R: 340-352, I: 440-452, Z: 540-552, NB: 600-693
+# mosaic2:  B: 160-167, V: 260-267, VR: 360-367, R: 460-467, I: 560-567
+# PS1 ISP:  g: 3200,      r: 3300,      i: 3400,      z: 3500,      y: 3600
+# PS1 TC3:  g: 3280-3296, r: 3380-3396, i: 3480-3496, z: 3580-3596, y: 3680-3696, 
+# PS1 GPC1: g: 3201-3264, r: 3301-3364, i: 3401-3464, z: 3501-3564, y: 3601-3664
+
+# external references: 1000 - 2999
+
+#                                                                                       astrometry  photometry     astr mask      phot mask
+# code name		    type  zero  airmass offset  c1    c2  slope  <color> eq     sys  scale  sys  scale     poor   bad     poor   bad 
+  1     g                    sec   0.000  0.000 0.000  1003  1004 0.0160     0  1051   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  2     r                    sec   0.000  0.000 0.000  1003  1004 0.0080     0  1052   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  3     i                    sec   0.000  0.000 0.000  1003  1004 0.0280     0  1053   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  4     z                    sec   0.000  0.000 0.000  1003  1004 0.1070     0  1054   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  5     y                    sec   0.000  0.000 0.000     -     - 0.0000     0  1054   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+
+  6     J                    sec   0.000  0.000 0.000     -     - 0.0000     0  2011   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  7     H                    sec   0.000  0.000 0.000     -     - 0.0000     0  2012   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  8     K                    sec   0.000  0.000 0.000     -     - 0.0000     0  2013   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+
+
+  1010  U_L92                ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1011  B_L92                ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1012  V_L92                ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1013  R_L92                ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1014  I_L92                ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+
+  1020  U_STET               ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1021  B_STET               ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1022  V_STET               ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1023  R_STET               ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1024  I_STET               ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+
+  1050  u_SDSS               ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1051  g_SDSS               ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1052  r_SDSS               ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1053  i_SDSS               ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1054  z_SDSS               ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+
+  1055  U_SDSS               dep  25.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1056  G_SDSS               dep  25.000  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1057  R_SDSS               dep  25.000  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1058  I_SDSS               dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1059  Z_SDSS               dep  25.000  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+
+# the 30 different SDSS photcodes for the SDSSmosaic camera... 6 columns of CCDs (camcols) x 5 filters.
+# Added by Sebastian Jester jester@mpia.de
+# These likely need work!
+  1060  SDSS.r.11            dep   0.000  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
+  1061  SDSS.r.12            dep   0.000  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
+  1062  SDSS.r.13            dep   0.000  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
+  1063  SDSS.r.14            dep   0.000  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
+  1064  SDSS.r.15            dep   0.000  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
+  1065  SDSS.r.16            dep   0.000  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
+  1066  SDSS.i.21            dep   0.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
+  1067  SDSS.i.22            dep   0.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
+  1068  SDSS.i.23            dep   0.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
+  1069  SDSS.i.24            dep   0.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
+  1070  SDSS.i.25            dep   0.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
+  1071  SDSS.i.26            dep   0.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
+  1072  SDSS.u.31            dep   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
+  1073  SDSS.u.32            dep   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
+  1074  SDSS.u.33            dep   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
+  1075  SDSS.u.34            dep   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
+  1076  SDSS.u.35            dep   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
+  1077  SDSS.u.36            dep   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
+  1078  SDSS.z.41            dep   0.000  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
+  1079  SDSS.z.42            dep   0.000  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
+  1080  SDSS.z.43            dep   0.000  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
+  1081  SDSS.z.44            dep   0.000  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
+  1082  SDSS.z.45            dep   0.000  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
+  1083  SDSS.z.46            dep   0.000  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
+  1084  SDSS.g.51            dep   0.000  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
+  1085  SDSS.g.52            dep   0.000  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
+  1086  SDSS.g.53            dep   0.000  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
+  1087  SDSS.g.54            dep   0.000  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
+  1088  SDSS.g.55            dep   0.000  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
+  1089  SDSS.g.56            dep   0.000  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000
+
+  1100  GSC                  ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+
+  1000  USNO_BLUE            ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1001  USNO_RED             ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1002  USNO_J               ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1003  USNO_F               ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1004  USNO_N               ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+
+  1110  USNO.098.RED         dep  20.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1111  USNO.098-0.RED70     dep  20.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1112  USNO.098-0.RG630     dep  20.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1113  USNO.103AD.MULTI     dep  20.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1114  USNO.103AD.YEL3      dep  20.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1115  USNO.103AD.YEL8      dep  20.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1116  USNO.103AE.#12       dep  20.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1117  USNO.103AE.AMB2      dep  20.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1118  USNO.103AE.AMB3      dep  20.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1119  USNO.103AE.AMB4      dep  20.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1120  USNO.103AE.AMB5      dep  20.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1121  USNO.103AE.AMB6      dep  20.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1122  USNO.103AE.AMB7      dep  20.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1123  USNO.103AE.AMB8      dep  20.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1124  USNO.103AE.NONE      dep  20.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1125  USNO.103AE.RED66     dep  20.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1126  USNO.103AE.RED67     dep  20.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1127  USNO.103AE.RED68     dep  20.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1128  USNO.103AE.RED69     dep  20.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1129  USNO.103AE.RED70     dep  20.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1130  USNO.103AE.RED71     dep  20.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1131  USNO.103AE.RED73     dep  20.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1132  USNO.103AE.RG2444    dep  20.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1133  USNO.103AE.RP2444    dep  20.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1134  USNO.103AO.NONE      dep  20.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1135  USNO.IIIAF.OG590     dep  20.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1136  USNO.IIIAF.RG600     dep  20.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1137  USNO.IIIAF.RG610     dep  20.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1138  USNO.IIIAF.RG630     dep  20.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1139  USNO.IIIAJ.GG358     dep  20.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1140  USNO.IIIAJ.GG385     dep  20.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1141  USNO.IIIAJ.GG395     dep  20.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1142  USNO.IVN.RG715       dep  20.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1143  USNO.IVN.RG9         dep  20.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  1144  USNO.IVN.WR88A       dep  20.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+
+  2020  TYCHO_B              ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  2021  TYCHO_V              ref   0.000  0.000 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+
+  2011  2MASS_J              ref   0.000  0.000 0.000     -     - 0.0000     0     6   0.050 0.000 1.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  2012  2MASS_H              ref   0.000  0.000 0.000     -     - 0.0000     0     7   0.050 0.000 1.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  2013  2MASS_K              ref   0.000  0.000 0.000     -     - 0.0000     0     8   0.050 0.000 1.000  0.000   0x0000 0x0000  0x0000 0x0000	
+
+# these were used for the synthetic photometry catalog based on 2mass					       										
+  3001  PS_g                 ref   0.000  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  3002  PS_r                 ref   0.000  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  3003  PS_i                 ref   0.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  3004  PS_z                 ref   0.000  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  3005  PS_y                 ref   0.000  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+
+### megacam photcodes:
+  100   MEGACAM.u.00         dep  25.280 -0.350 0.000     -     - 0.0000     0     -   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  101   MEGACAM.u.01         dep  25.280 -0.350 0.000     -     - 0.0000     0     -   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  102   MEGACAM.u.02         dep  25.280 -0.350 0.000     -     - 0.0000     0     -   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  103   MEGACAM.u.03         dep  25.280 -0.350 0.000     -     - 0.0000     0     -   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  104   MEGACAM.u.04         dep  25.280 -0.350 0.000     -     - 0.0000     0     -   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  105   MEGACAM.u.05         dep  25.280 -0.350 0.000     -     - 0.0000     0     -   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  106   MEGACAM.u.06         dep  25.280 -0.350 0.000     -     - 0.0000     0     -   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  107   MEGACAM.u.07         dep  25.280 -0.350 0.000     -     - 0.0000     0     -   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  108   MEGACAM.u.08         dep  25.280 -0.350 0.000     -     - 0.0000     0     -   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  109   MEGACAM.u.09         dep  25.280 -0.350 0.000     -     - 0.0000     0     -   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  110   MEGACAM.u.10         dep  25.280 -0.350 0.000     -     - 0.0000     0     -   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  111   MEGACAM.u.11         dep  25.280 -0.350 0.000     -     - 0.0000     0     -   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  112   MEGACAM.u.12         dep  25.280 -0.350 0.000     -     - 0.0000     0     -   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  113   MEGACAM.u.13         dep  25.280 -0.350 0.000     -     - 0.0000     0     -   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  114   MEGACAM.u.14         dep  25.280 -0.350 0.000     -     - 0.0000     0     -   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  115   MEGACAM.u.15         dep  25.280 -0.350 0.000     -     - 0.0000     0     -   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  116   MEGACAM.u.16         dep  25.280 -0.350 0.000     -     - 0.0000     0     -   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  117   MEGACAM.u.17         dep  25.280 -0.350 0.000     -     - 0.0000     0     -   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  118   MEGACAM.u.18         dep  25.280 -0.350 0.000     -     - 0.0000     0     -   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  119   MEGACAM.u.19         dep  25.280 -0.350 0.000     -     - 0.0000     0     -   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  120   MEGACAM.u.20         dep  25.280 -0.350 0.000     -     - 0.0000     0     -   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  121   MEGACAM.u.21         dep  25.280 -0.350 0.000     -     - 0.0000     0     -   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  122   MEGACAM.u.22         dep  25.280 -0.350 0.000     -     - 0.0000     0     -   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  123   MEGACAM.u.23         dep  25.280 -0.350 0.000     -     - 0.0000     0     -   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  124   MEGACAM.u.24         dep  25.280 -0.350 0.000     -     - 0.0000     0     -   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  125   MEGACAM.u.25         dep  25.280 -0.350 0.000     -     - 0.0000     0     -   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  126   MEGACAM.u.26         dep  25.280 -0.350 0.000     -     - 0.0000     0     -   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  127   MEGACAM.u.27         dep  25.280 -0.350 0.000     -     - 0.0000     0     -   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  128   MEGACAM.u.28         dep  25.280 -0.350 0.000     -     - 0.0000     0     -   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  129   MEGACAM.u.29         dep  25.280 -0.350 0.000     -     - 0.0000     0     -   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  130   MEGACAM.u.30         dep  25.280 -0.350 0.000     -     - 0.0000     0     -   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  131   MEGACAM.u.31         dep  25.280 -0.350 0.000     -     - 0.0000     0     -   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  132   MEGACAM.u.32         dep  25.280 -0.350 0.000     -     - 0.0000     0     -   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  133   MEGACAM.u.33         dep  25.280 -0.350 0.000     -     - 0.0000     0     -   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  134   MEGACAM.u.34         dep  25.280 -0.350 0.000     -     - 0.0000     0     -   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  135   MEGACAM.u.35         dep  25.280 -0.350 0.000     -     - 0.0000     0     -   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  136   MEGACAM.u.36         dep  25.280 -0.350 0.000     -     - 0.0000     0     -   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  137   MEGACAM.u.37         dep  25.280 -0.350 0.000     -     - 0.0000     0     -   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  138   MEGACAM.u.38         dep  25.280 -0.350 0.000     -     - 0.0000     0     -   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  139   MEGACAM.u.39         dep  25.280 -0.350 0.000     -     - 0.0000     0     -   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+
+  200   MEGACAM.g.00         dep  26.460 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  201   MEGACAM.g.01         dep  26.460 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  202   MEGACAM.g.02         dep  26.460 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  203   MEGACAM.g.03         dep  26.460 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  204   MEGACAM.g.04         dep  26.460 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  205   MEGACAM.g.05         dep  26.460 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  206   MEGACAM.g.06         dep  26.460 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  207   MEGACAM.g.07         dep  26.460 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  208   MEGACAM.g.08         dep  26.460 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  209   MEGACAM.g.09         dep  26.460 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  210   MEGACAM.g.10         dep  26.460 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  211   MEGACAM.g.11         dep  26.460 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  212   MEGACAM.g.12         dep  26.460 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  213   MEGACAM.g.13         dep  26.460 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  214   MEGACAM.g.14         dep  26.460 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  215   MEGACAM.g.15         dep  26.460 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  216   MEGACAM.g.16         dep  26.460 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  217   MEGACAM.g.17         dep  26.460 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  218   MEGACAM.g.18         dep  26.460 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  219   MEGACAM.g.19         dep  26.460 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  220   MEGACAM.g.20         dep  26.460 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  221   MEGACAM.g.21         dep  26.460 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  222   MEGACAM.g.22         dep  26.460 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  223   MEGACAM.g.23         dep  26.460 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  224   MEGACAM.g.24         dep  26.460 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  225   MEGACAM.g.25         dep  26.460 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  226   MEGACAM.g.26         dep  26.460 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  227   MEGACAM.g.27         dep  26.460 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  228   MEGACAM.g.28         dep  26.460 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  229   MEGACAM.g.29         dep  26.460 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  230   MEGACAM.g.30         dep  26.460 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  231   MEGACAM.g.31         dep  26.460 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  232   MEGACAM.g.32         dep  26.460 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  233   MEGACAM.g.33         dep  26.460 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  234   MEGACAM.g.34         dep  26.460 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  235   MEGACAM.g.35         dep  26.460 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  236   MEGACAM.g.36         dep  26.460 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  237   MEGACAM.g.37         dep  26.460 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  238   MEGACAM.g.38         dep  26.460 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  239   MEGACAM.g.39         dep  26.460 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+
+  300   MEGACAM.r.00         dep  25.978 -0.100 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  301   MEGACAM.r.01         dep  25.978 -0.100 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  302   MEGACAM.r.02         dep  25.978 -0.100 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  303   MEGACAM.r.03         dep  25.978 -0.100 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  304   MEGACAM.r.04         dep  25.978 -0.100 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  305   MEGACAM.r.05         dep  25.978 -0.100 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  306   MEGACAM.r.06         dep  25.978 -0.100 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  307   MEGACAM.r.07         dep  25.978 -0.100 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  308   MEGACAM.r.08         dep  25.978 -0.100 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  309   MEGACAM.r.09         dep  25.978 -0.100 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  310   MEGACAM.r.10         dep  25.978 -0.100 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  311   MEGACAM.r.11         dep  25.978 -0.100 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  312   MEGACAM.r.12         dep  25.978 -0.100 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  313   MEGACAM.r.13         dep  25.978 -0.100 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  314   MEGACAM.r.14         dep  25.978 -0.100 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  315   MEGACAM.r.15         dep  25.978 -0.100 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  316   MEGACAM.r.16         dep  25.978 -0.100 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  317   MEGACAM.r.17         dep  25.978 -0.100 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  318   MEGACAM.r.18         dep  25.978 -0.100 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  319   MEGACAM.r.19         dep  25.978 -0.100 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  320   MEGACAM.r.20         dep  25.978 -0.100 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  321   MEGACAM.r.21         dep  25.978 -0.100 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  322   MEGACAM.r.22         dep  25.978 -0.100 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  323   MEGACAM.r.23         dep  25.978 -0.100 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  324   MEGACAM.r.24         dep  25.978 -0.100 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  325   MEGACAM.r.25         dep  25.978 -0.100 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  326   MEGACAM.r.26         dep  25.978 -0.100 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  327   MEGACAM.r.27         dep  25.978 -0.100 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  328   MEGACAM.r.28         dep  25.978 -0.100 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  329   MEGACAM.r.29         dep  25.978 -0.100 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  330   MEGACAM.r.30         dep  25.978 -0.100 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  331   MEGACAM.r.31         dep  25.978 -0.100 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  332   MEGACAM.r.32         dep  25.978 -0.100 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  333   MEGACAM.r.33         dep  25.978 -0.100 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  334   MEGACAM.r.34         dep  25.978 -0.100 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  335   MEGACAM.r.35         dep  25.978 -0.100 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  336   MEGACAM.r.36         dep  25.978 -0.100 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  337   MEGACAM.r.37         dep  25.978 -0.100 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  338   MEGACAM.r.38         dep  25.978 -0.100 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  339   MEGACAM.r.39         dep  25.978 -0.100 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+
+  400   MEGACAM.i.00         dep  25.744 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  401   MEGACAM.i.01         dep  25.744 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  402   MEGACAM.i.02         dep  25.744 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  403   MEGACAM.i.03         dep  25.744 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  404   MEGACAM.i.04         dep  25.744 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  405   MEGACAM.i.05         dep  25.744 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  406   MEGACAM.i.06         dep  25.744 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  407   MEGACAM.i.07         dep  25.744 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  408   MEGACAM.i.08         dep  25.744 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  409   MEGACAM.i.09         dep  25.744 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  410   MEGACAM.i.10         dep  25.744 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  411   MEGACAM.i.11         dep  25.744 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  412   MEGACAM.i.12         dep  25.744 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  413   MEGACAM.i.13         dep  25.744 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  414   MEGACAM.i.14         dep  25.744 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  415   MEGACAM.i.15         dep  25.744 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  416   MEGACAM.i.16         dep  25.744 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  417   MEGACAM.i.17         dep  25.744 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  418   MEGACAM.i.18         dep  25.744 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  419   MEGACAM.i.19         dep  25.744 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  420   MEGACAM.i.20         dep  25.744 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  421   MEGACAM.i.21         dep  25.744 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  422   MEGACAM.i.22         dep  25.744 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  423   MEGACAM.i.23         dep  25.744 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  424   MEGACAM.i.24         dep  25.744 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  425   MEGACAM.i.25         dep  25.744 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  426   MEGACAM.i.26         dep  25.744 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  427   MEGACAM.i.27         dep  25.744 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  428   MEGACAM.i.28         dep  25.744 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  429   MEGACAM.i.29         dep  25.744 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  430   MEGACAM.i.30         dep  25.744 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  431   MEGACAM.i.31         dep  25.744 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  432   MEGACAM.i.32         dep  25.744 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  433   MEGACAM.i.33         dep  25.744 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  434   MEGACAM.i.34         dep  25.744 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  435   MEGACAM.i.35         dep  25.744 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  436   MEGACAM.i.36         dep  25.744 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  437   MEGACAM.i.37         dep  25.744 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  438   MEGACAM.i.38         dep  25.744 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  439   MEGACAM.i.39         dep  25.744 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+
+  500   MEGACAM.z.00         dep  24.800 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  501   MEGACAM.z.01         dep  24.800 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  502   MEGACAM.z.02         dep  24.800 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  503   MEGACAM.z.03         dep  24.800 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  504   MEGACAM.z.04         dep  24.800 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  505   MEGACAM.z.05         dep  24.800 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  506   MEGACAM.z.06         dep  24.800 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  507   MEGACAM.z.07         dep  24.800 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  508   MEGACAM.z.08         dep  24.800 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  509   MEGACAM.z.09         dep  24.800 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  510   MEGACAM.z.10         dep  24.800 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  511   MEGACAM.z.11         dep  24.800 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  512   MEGACAM.z.12         dep  24.800 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  513   MEGACAM.z.13         dep  24.800 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  514   MEGACAM.z.14         dep  24.800 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  515   MEGACAM.z.15         dep  24.800 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  516   MEGACAM.z.16         dep  24.800 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  517   MEGACAM.z.17         dep  24.800 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  518   MEGACAM.z.18         dep  24.800 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  519   MEGACAM.z.19         dep  24.800 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  520   MEGACAM.z.20         dep  24.800 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  521   MEGACAM.z.21         dep  24.800 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  522   MEGACAM.z.22         dep  24.800 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  523   MEGACAM.z.23         dep  24.800 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  524   MEGACAM.z.24         dep  24.800 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  525   MEGACAM.z.25         dep  24.800 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  526   MEGACAM.z.26         dep  24.800 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  527   MEGACAM.z.27         dep  24.800 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  528   MEGACAM.z.28         dep  24.800 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  529   MEGACAM.z.29         dep  24.800 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  530   MEGACAM.z.30         dep  24.800 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  531   MEGACAM.z.31         dep  24.800 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  532   MEGACAM.z.32         dep  24.800 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  533   MEGACAM.z.33         dep  24.800 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  534   MEGACAM.z.34         dep  24.800 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  535   MEGACAM.z.35         dep  24.800 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  536   MEGACAM.z.36         dep  24.800 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  537   MEGACAM.z.37         dep  24.800 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  538   MEGACAM.z.38         dep  24.800 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  539   MEGACAM.z.39         dep  24.800 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+
+### cfh12k photcodes
+  140   CFH12K.B.00          dep  26.000 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  141   CFH12K.B.01          dep  26.000 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  142   CFH12K.B.02          dep  26.000 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  143   CFH12K.B.03          dep  26.000 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  144   CFH12K.B.04          dep  26.000 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  145   CFH12K.B.05          dep  26.000 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  146   CFH12K.B.06          dep  26.000 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  147   CFH12K.B.07          dep  26.000 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  148   CFH12K.B.08          dep  26.000 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  149   CFH12K.B.09          dep  26.000 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  150   CFH12K.B.10          dep  26.000 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  151   CFH12K.B.11          dep  26.000 -0.150 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+
+  240   CFH12K.V.00          dep  26.150 -0.120 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  241   CFH12K.V.01          dep  26.150 -0.120 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  242   CFH12K.V.02          dep  26.150 -0.120 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  243   CFH12K.V.03          dep  26.150 -0.120 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  244   CFH12K.V.04          dep  26.150 -0.120 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  245   CFH12K.V.05          dep  26.150 -0.120 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  246   CFH12K.V.06          dep  26.150 -0.120 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  247   CFH12K.V.07          dep  26.150 -0.120 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  248   CFH12K.V.08          dep  26.150 -0.120 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  249   CFH12K.V.09          dep  26.150 -0.120 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  250   CFH12K.V.10          dep  26.150 -0.120 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  251   CFH12K.V.11          dep  26.150 -0.120 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+
+  340   CFH12K.R.00          dep  26.190 -0.090 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  341   CFH12K.R.01          dep  26.190 -0.090 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  342   CFH12K.R.02          dep  26.190 -0.090 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  343   CFH12K.R.03          dep  26.190 -0.090 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  344   CFH12K.R.04          dep  26.190 -0.090 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  345   CFH12K.R.05          dep  26.190 -0.090 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  346   CFH12K.R.06          dep  26.190 -0.090 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  347   CFH12K.R.07          dep  26.190 -0.090 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  348   CFH12K.R.08          dep  26.190 -0.090 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  349   CFH12K.R.09          dep  26.190 -0.090 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  350   CFH12K.R.10          dep  26.190 -0.090 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  351   CFH12K.R.11          dep  26.190 -0.090 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+
+  440   CFH12K.I.00          dep  26.135 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  441   CFH12K.I.01          dep  26.135 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  442   CFH12K.I.02          dep  26.135 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  443   CFH12K.I.03          dep  26.135 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  444   CFH12K.I.04          dep  26.135 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  445   CFH12K.I.05          dep  26.135 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  446   CFH12K.I.06          dep  26.135 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  447   CFH12K.I.07          dep  26.135 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  448   CFH12K.I.08          dep  26.135 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  449   CFH12K.I.09          dep  26.135 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  450   CFH12K.I.10          dep  26.135 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  451   CFH12K.I.11          dep  26.135 -0.040 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+
+  540   CFH12K.Z.00          dep  25.830 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  541   CFH12K.Z.01          dep  25.830 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  542   CFH12K.Z.02          dep  25.830 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  543   CFH12K.Z.03          dep  25.830 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  544   CFH12K.Z.04          dep  25.830 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  545   CFH12K.Z.05          dep  25.830 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  546   CFH12K.Z.06          dep  25.830 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  547   CFH12K.Z.07          dep  25.830 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  548   CFH12K.Z.08          dep  25.830 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  549   CFH12K.Z.09          dep  25.830 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  550   CFH12K.Z.10          dep  25.830 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  551   CFH12K.Z.11          dep  25.830 -0.030 0.000     -     - 0.0000     0     4   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+
+  600   CFH12K.Ha.00         dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  601   CFH12K.Ha.01         dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  602   CFH12K.Ha.02         dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  603   CFH12K.Ha.03         dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  604   CFH12K.Ha.04         dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  605   CFH12K.Ha.05         dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  606   CFH12K.Ha.06         dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  607   CFH12K.Ha.07         dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  608   CFH12K.Ha.08         dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  609   CFH12K.Ha.09         dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  610   CFH12K.Ha.10         dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  611   CFH12K.Ha.11         dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+
+  612   CFH12K.HaOff.00      dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  613   CFH12K.HaOff.01      dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  614   CFH12K.HaOff.02      dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  615   CFH12K.HaOff.03      dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  616   CFH12K.HaOff.04      dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  617   CFH12K.HaOff.05      dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  618   CFH12K.HaOff.06      dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  619   CFH12K.HaOff.07      dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  620   CFH12K.HaOff.08      dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  621   CFH12K.HaOff.09      dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  622   CFH12K.HaOff.10      dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  623   CFH12K.HaOff.11      dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  624   CFH12K.CN.00         dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  625   CFH12K.CN.01         dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  626   CFH12K.CN.02         dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  627   CFH12K.CN.03         dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  628   CFH12K.CN.04         dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  629   CFH12K.CN.05         dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  630   CFH12K.CN.06         dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  631   CFH12K.CN.07         dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  632   CFH12K.CN.08         dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  633   CFH12K.CN.09         dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  634   CFH12K.CN.10         dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  635   CFH12K.CN.11         dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  636   CFH12K.TiO.00        dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  637   CFH12K.TiO.01        dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  638   CFH12K.TiO.02        dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  639   CFH12K.TiO.03        dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  640   CFH12K.TiO.04        dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  641   CFH12K.TiO.05        dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  642   CFH12K.TiO.06        dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  643   CFH12K.TiO.07        dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  644   CFH12K.TiO.08        dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  645   CFH12K.TiO.09        dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  646   CFH12K.TiO.10        dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  647   CFH12K.TiO.11        dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  648   CFH12K.NB920.00      dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  649   CFH12K.NB920.01      dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  650   CFH12K.NB920.02      dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  651   CFH12K.NB920.03      dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  652   CFH12K.NB920.04      dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  653   CFH12K.NB920.05      dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  654   CFH12K.NB920.06      dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  655   CFH12K.NB920.07      dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  678   CFH12K.NB920.08      dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  679   CFH12K.NB920.09      dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  680   CFH12K.NB920.10      dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  681   CFH12K.NB920.11      dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  682   CFH12K.B2F.00        dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  683   CFH12K.B2F.01        dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  684   CFH12K.B2F.02        dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  685   CFH12K.B2F.03        dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  686   CFH12K.B2F.04        dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  687   CFH12K.B2F.05        dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  688   CFH12K.B2F.06        dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  689   CFH12K.B2F.07        dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  690   CFH12K.B2F.08        dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  691   CFH12K.B2F.09        dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  692   CFH12K.B2F.10        dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  693   CFH12K.B2F.11        dep  25.000  0.040 0.000     -     - 0.0000     0     -   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+
+  3200  ISP-Apogee-01.g      dep  19.250  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  3300  ISP-Apogee-01.r      dep  19.150  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  3400  ISP-Apogee-01.i      dep  18.500  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  3500  ISP-Apogee-01.z      dep  17.250  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  3600  ISP-Apogee-01.y      dep  14.150  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+
+  4100  SIMTEST.g.Chip       dep  25.000  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  4200  SIMTEST.r.Chip       dep  25.000  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  4300  SIMTEST.i.Chip       dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  4400  SIMTEST.z.Chip       dep  25.000  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  4500  SIMTEST.y.Chip       dep  25.000  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+
+ 14100  SIMTEST.g.SkyChip    dep  25.000  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+ 14200  SIMTEST.r.SkyChip    dep  25.000  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+ 14300  SIMTEST.i.SkyChip    dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+ 14400  SIMTEST.z.SkyChip    dep  25.000  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+ 14500  SIMTEST.y.SkyChip    dep  25.000  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+
+
+  4101  SIMMOSAIC.g.00       dep  25.000  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  4102  SIMMOSAIC.g.01       dep  25.000  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  4103  SIMMOSAIC.g.02       dep  25.000  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  4104  SIMMOSAIC.g.03       dep  25.000  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  4201  SIMMOSAIC.r.00       dep  25.000  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  4202  SIMMOSAIC.r.01       dep  25.000  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  4203  SIMMOSAIC.r.02       dep  25.000  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  4204  SIMMOSAIC.r.03       dep  25.000  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  4301  SIMMOSAIC.i.00       dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  4302  SIMMOSAIC.i.01       dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  4303  SIMMOSAIC.i.02       dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  4304  SIMMOSAIC.i.03       dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  4401  SIMMOSAIC.z.00       dep  25.000  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  4402  SIMMOSAIC.z.01       dep  25.000  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  4403  SIMMOSAIC.z.02       dep  25.000  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  4404  SIMMOSAIC.z.03       dep  25.000  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  4501  SIMMOSAIC.y.00       dep  25.000  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  4502  SIMMOSAIC.y.01       dep  25.000  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  4503  SIMMOSAIC.y.02       dep  25.000  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  4504  SIMMOSAIC.y.03       dep  25.000  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+
+# 2007.10.02 : predicted g zero-points from 2006 NSF proposal						       											
+  10001 GPC1.g.XY01          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10002 GPC1.g.XY02          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10003 GPC1.g.XY03          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10004 GPC1.g.XY04          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10005 GPC1.g.XY05          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10006 GPC1.g.XY06          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10010 GPC1.g.XY10          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10011 GPC1.g.XY11          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10012 GPC1.g.XY12          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10013 GPC1.g.XY13          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10014 GPC1.g.XY14          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10015 GPC1.g.XY15          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10016 GPC1.g.XY16          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10017 GPC1.g.XY17          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10020 GPC1.g.XY20          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10021 GPC1.g.XY21          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10022 GPC1.g.XY22          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10023 GPC1.g.XY23          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10024 GPC1.g.XY24          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10025 GPC1.g.XY25          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10026 GPC1.g.XY26          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10027 GPC1.g.XY27          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10030 GPC1.g.XY30          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10031 GPC1.g.XY31          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10032 GPC1.g.XY32          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10033 GPC1.g.XY33          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10034 GPC1.g.XY34          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10035 GPC1.g.XY35          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10036 GPC1.g.XY36          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10037 GPC1.g.XY37          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10040 GPC1.g.XY40          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10041 GPC1.g.XY41          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10042 GPC1.g.XY42          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10043 GPC1.g.XY43          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10044 GPC1.g.XY44          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10045 GPC1.g.XY45          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10046 GPC1.g.XY46          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10047 GPC1.g.XY47          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10050 GPC1.g.XY50          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10051 GPC1.g.XY51          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10052 GPC1.g.XY52          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10053 GPC1.g.XY53          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10054 GPC1.g.XY54          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10055 GPC1.g.XY55          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10056 GPC1.g.XY56          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10057 GPC1.g.XY57          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10060 GPC1.g.XY60          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10061 GPC1.g.XY61          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10062 GPC1.g.XY62          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10063 GPC1.g.XY63          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10064 GPC1.g.XY64          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10065 GPC1.g.XY65          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10066 GPC1.g.XY66          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10067 GPC1.g.XY67          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10071 GPC1.g.XY71          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10072 GPC1.g.XY72          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10073 GPC1.g.XY73          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10074 GPC1.g.XY74          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10075 GPC1.g.XY75          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10076 GPC1.g.XY76          dep  24.900  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+
+# 2007.10.02 : predicted r zero-points from 2006 NSF proposal						       											
+  10101 GPC1.r.XY01          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10102 GPC1.r.XY02          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10103 GPC1.r.XY03          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10104 GPC1.r.XY04          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10105 GPC1.r.XY05          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10106 GPC1.r.XY06          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10110 GPC1.r.XY10          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10111 GPC1.r.XY11          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10112 GPC1.r.XY12          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10113 GPC1.r.XY13          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10114 GPC1.r.XY14          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10115 GPC1.r.XY15          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10116 GPC1.r.XY16          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10117 GPC1.r.XY17          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10120 GPC1.r.XY20          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10121 GPC1.r.XY21          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10122 GPC1.r.XY22          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10123 GPC1.r.XY23          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10124 GPC1.r.XY24          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10125 GPC1.r.XY25          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10126 GPC1.r.XY26          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10127 GPC1.r.XY27          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10130 GPC1.r.XY30          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10131 GPC1.r.XY31          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10132 GPC1.r.XY32          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10133 GPC1.r.XY33          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10134 GPC1.r.XY34          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10135 GPC1.r.XY35          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10136 GPC1.r.XY36          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10137 GPC1.r.XY37          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10140 GPC1.r.XY40          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10141 GPC1.r.XY41          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10142 GPC1.r.XY42          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10143 GPC1.r.XY43          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10144 GPC1.r.XY44          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10145 GPC1.r.XY45          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10146 GPC1.r.XY46          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10147 GPC1.r.XY47          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10150 GPC1.r.XY50          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10151 GPC1.r.XY51          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10152 GPC1.r.XY52          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10153 GPC1.r.XY53          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10154 GPC1.r.XY54          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10155 GPC1.r.XY55          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10156 GPC1.r.XY56          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10157 GPC1.r.XY57          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10160 GPC1.r.XY60          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10161 GPC1.r.XY61          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10162 GPC1.r.XY62          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10163 GPC1.r.XY63          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10164 GPC1.r.XY64          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10165 GPC1.r.XY65          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10166 GPC1.r.XY66          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10167 GPC1.r.XY67          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10171 GPC1.r.XY71          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10172 GPC1.r.XY72          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10173 GPC1.r.XY73          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10174 GPC1.r.XY74          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10175 GPC1.r.XY75          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10176 GPC1.r.XY76          dep  25.100  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+
+# 2007.10.02 : predicted i zero-points from 2006 NSF proposal						       											
+  10201 GPC1.i.XY01          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10202 GPC1.i.XY02          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10203 GPC1.i.XY03          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10204 GPC1.i.XY04          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10205 GPC1.i.XY05          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10206 GPC1.i.XY06          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10210 GPC1.i.XY10          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10211 GPC1.i.XY11          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10212 GPC1.i.XY12          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10213 GPC1.i.XY13          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10214 GPC1.i.XY14          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10215 GPC1.i.XY15          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10216 GPC1.i.XY16          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10217 GPC1.i.XY17          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10220 GPC1.i.XY20          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10221 GPC1.i.XY21          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10222 GPC1.i.XY22          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10223 GPC1.i.XY23          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10224 GPC1.i.XY24          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10225 GPC1.i.XY25          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10226 GPC1.i.XY26          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10227 GPC1.i.XY27          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10230 GPC1.i.XY30          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10231 GPC1.i.XY31          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10232 GPC1.i.XY32          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10233 GPC1.i.XY33          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10234 GPC1.i.XY34          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10235 GPC1.i.XY35          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10236 GPC1.i.XY36          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10237 GPC1.i.XY37          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10240 GPC1.i.XY40          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10241 GPC1.i.XY41          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10242 GPC1.i.XY42          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10243 GPC1.i.XY43          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10244 GPC1.i.XY44          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10245 GPC1.i.XY45          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10246 GPC1.i.XY46          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10247 GPC1.i.XY47          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10250 GPC1.i.XY50          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10251 GPC1.i.XY51          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10252 GPC1.i.XY52          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10253 GPC1.i.XY53          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10254 GPC1.i.XY54          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10255 GPC1.i.XY55          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10256 GPC1.i.XY56          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10257 GPC1.i.XY57          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10260 GPC1.i.XY60          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10261 GPC1.i.XY61          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10262 GPC1.i.XY62          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10263 GPC1.i.XY63          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10264 GPC1.i.XY64          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10265 GPC1.i.XY65          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10266 GPC1.i.XY66          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10267 GPC1.i.XY67          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10271 GPC1.i.XY71          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10272 GPC1.i.XY72          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10273 GPC1.i.XY73          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10274 GPC1.i.XY74          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10275 GPC1.i.XY75          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10276 GPC1.i.XY76          dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+
+# 2007.10.02 : predicted z zero-points from 2006 NSF proposal						       											
+  10301 GPC1.z.XY01          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10302 GPC1.z.XY02          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10303 GPC1.z.XY03          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10304 GPC1.z.XY04          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10305 GPC1.z.XY05          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10306 GPC1.z.XY06          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10310 GPC1.z.XY10          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10311 GPC1.z.XY11          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10312 GPC1.z.XY12          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10313 GPC1.z.XY13          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10314 GPC1.z.XY14          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10315 GPC1.z.XY15          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10316 GPC1.z.XY16          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10317 GPC1.z.XY17          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10320 GPC1.z.XY20          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10321 GPC1.z.XY21          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10322 GPC1.z.XY22          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10323 GPC1.z.XY23          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10324 GPC1.z.XY24          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10325 GPC1.z.XY25          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10326 GPC1.z.XY26          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10327 GPC1.z.XY27          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10330 GPC1.z.XY30          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10331 GPC1.z.XY31          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10332 GPC1.z.XY32          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10333 GPC1.z.XY33          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10334 GPC1.z.XY34          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10335 GPC1.z.XY35          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10336 GPC1.z.XY36          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10337 GPC1.z.XY37          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10340 GPC1.z.XY40          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10341 GPC1.z.XY41          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10342 GPC1.z.XY42          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10343 GPC1.z.XY43          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10344 GPC1.z.XY44          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10345 GPC1.z.XY45          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10346 GPC1.z.XY46          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10347 GPC1.z.XY47          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10350 GPC1.z.XY50          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10351 GPC1.z.XY51          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10352 GPC1.z.XY52          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10353 GPC1.z.XY53          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10354 GPC1.z.XY54          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10355 GPC1.z.XY55          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10356 GPC1.z.XY56          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10357 GPC1.z.XY57          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10360 GPC1.z.XY60          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10361 GPC1.z.XY61          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10362 GPC1.z.XY62          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10363 GPC1.z.XY63          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10364 GPC1.z.XY64          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10365 GPC1.z.XY65          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10366 GPC1.z.XY66          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10367 GPC1.z.XY67          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10371 GPC1.z.XY71          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10372 GPC1.z.XY72          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10373 GPC1.z.XY73          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10374 GPC1.z.XY74          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10375 GPC1.z.XY75          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10376 GPC1.z.XY76          dep  24.600  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+													       										
+# 2007.10.02 : predicted y zero-points from 2006 NSF proposal						       											
+  10401 GPC1.y.XY01          dep  22.200  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10402 GPC1.y.XY02          dep  22.200  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10403 GPC1.y.XY03          dep  22.200  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10404 GPC1.y.XY04          dep  22.200  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10405 GPC1.y.XY05          dep  22.200  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10406 GPC1.y.XY06          dep  22.200  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10410 GPC1.y.XY10          dep  22.200  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10411 GPC1.y.XY11          dep  22.200  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10412 GPC1.y.XY12          dep  22.200  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10413 GPC1.y.XY13          dep  22.200  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10414 GPC1.y.XY14          dep  22.200  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10415 GPC1.y.XY15          dep  22.200  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10416 GPC1.y.XY16          dep  22.200  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10417 GPC1.y.XY17          dep  22.200  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10420 GPC1.y.XY20          dep  22.200  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10421 GPC1.y.XY21          dep  22.200  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10422 GPC1.y.XY22          dep  22.200  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10423 GPC1.y.XY23          dep  22.200  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10424 GPC1.y.XY24          dep  22.200  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10425 GPC1.y.XY25          dep  22.200  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10426 GPC1.y.XY26          dep  22.200  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10427 GPC1.y.XY27          dep  22.200  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10430 GPC1.y.XY30          dep  22.200  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10431 GPC1.y.XY31          dep  22.200  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10432 GPC1.y.XY32          dep  22.200  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10433 GPC1.y.XY33          dep  22.200  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10434 GPC1.y.XY34          dep  22.200  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10435 GPC1.y.XY35          dep  22.200  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10436 GPC1.y.XY36          dep  22.200  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10437 GPC1.y.XY37          dep  22.200  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10440 GPC1.y.XY40          dep  22.200  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10441 GPC1.y.XY41          dep  22.200  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10442 GPC1.y.XY42          dep  22.200  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10443 GPC1.y.XY43          dep  22.200  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10444 GPC1.y.XY44          dep  22.200  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10445 GPC1.y.XY45          dep  22.200  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10446 GPC1.y.XY46          dep  22.200  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10447 GPC1.y.XY47          dep  22.200  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10450 GPC1.y.XY50          dep  22.200  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10451 GPC1.y.XY51          dep  22.200  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10452 GPC1.y.XY52          dep  22.200  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10453 GPC1.y.XY53          dep  22.200  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10454 GPC1.y.XY54          dep  22.200  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10455 GPC1.y.XY55          dep  22.200  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10456 GPC1.y.XY56          dep  22.200  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10457 GPC1.y.XY57          dep  22.200  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10460 GPC1.y.XY60          dep  22.200  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10461 GPC1.y.XY61          dep  22.200  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10462 GPC1.y.XY62          dep  22.200  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10463 GPC1.y.XY63          dep  22.200  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10464 GPC1.y.XY64          dep  22.200  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10465 GPC1.y.XY65          dep  22.200  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10466 GPC1.y.XY66          dep  22.200  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10467 GPC1.y.XY67          dep  22.200  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10471 GPC1.y.XY71          dep  22.200  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10472 GPC1.y.XY72          dep  22.200  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10473 GPC1.y.XY73          dep  22.200  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10474 GPC1.y.XY74          dep  22.200  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10475 GPC1.y.XY75          dep  22.200  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  10476 GPC1.y.XY76          dep  22.200  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+
+  11000 GPC1.g.SkyChip       dep  25.000  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  11100 GPC1.r.SkyChip       dep  25.000  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  11200 GPC1.i.SkyChip       dep  25.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  11300 GPC1.z.SkyChip       dep  25.000  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+  11400 GPC1.y.SkyChip       dep  25.000  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000	
+
+# mosaic2 photcodes
+
+  160  MOSAIC2.B.00          dep  25.107  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  161  MOSAIC2.B.01          dep  25.120  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  162  MOSAIC2.B.02          dep  25.115  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  163  MOSAIC2.B.03          dep  25.111  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  164  MOSAIC2.B.04          dep  25.111  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  165  MOSAIC2.B.05          dep  25.101  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  166  MOSAIC2.B.06          dep  25.106  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  167  MOSAIC2.B.07          dep  25.104  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+
+  260  MOSAIC2.V.00          dep  25.448  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  261  MOSAIC2.V.01          dep  25.472  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  262  MOSAIC2.V.02          dep  25.472  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  263  MOSAIC2.V.03          dep  25.476  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  264  MOSAIC2.V.04          dep  25.475  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  265  MOSAIC2.V.05          dep  25.471  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  266  MOSAIC2.V.06          dep  25.472  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  267  MOSAIC2.V.07          dep  25.475  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+
+  360  MOSAIC2.VR.00         dep  25.636  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  361  MOSAIC2.VR.01         dep  25.633  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  362  MOSAIC2.VR.02         dep  25.648  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  363  MOSAIC2.VR.03         dep  25.632  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  364  MOSAIC2.VR.04         dep  25.628  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  365  MOSAIC2.VR.05         dep  25.616  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  366  MOSAIC2.VR.06         dep  25.629  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  367  MOSAIC2.VR.07         dep  25.624  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+
+  460  MOSAIC2.R.00          dep  25.636  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  461  MOSAIC2.R.01          dep  25.633  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  462  MOSAIC2.R.02          dep  25.648  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  463  MOSAIC2.R.03          dep  25.632  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  464  MOSAIC2.R.04          dep  25.628  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  465  MOSAIC2.R.05          dep  25.616  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  466  MOSAIC2.R.06          dep  25.629  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  467  MOSAIC2.R.07          dep  25.624  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+
+  560  MOSAIC2.I.00          dep  24.911  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  561  MOSAIC2.I.01          dep  24.882  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  562  MOSAIC2.I.02          dep  24.924  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  563  MOSAIC2.I.03          dep  24.906  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  564  MOSAIC2.I.04          dep  24.906  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  565  MOSAIC2.I.05          dep  24.877  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  566  MOSAIC2.I.06          dep  24.887  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
+  567  MOSAIC2.I.07          dep  24.912  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 1.000  0.000   0x0000 0x3888  0x0000 0x0000	
Index: /branches/cnb_branches/cnb_branch_20090301/ippconfig/gpc1/format_20080925.config
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippconfig/gpc1/format_20080925.config	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippconfig/gpc1/format_20080925.config	(revision 23352)
@@ -189,5 +189,5 @@
         FPA.RA          STR     RA
         FPA.DEC         STR     DEC
-        FPA.RADECSYS    STR     RADECSYS
+        FPA.RADECSYS    STR     EQUINOX
         FPA.OBSTYPE     STR     OBSTYPE
         FPA.OBJECT      STR     OBJECT
Index: /branches/cnb_branches/cnb_branch_20090301/ippconfig/gpc1/format_20080929.config
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippconfig/gpc1/format_20080929.config	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippconfig/gpc1/format_20080929.config	(revision 23352)
@@ -184,5 +184,5 @@
         FPA.RA          STR     RA
         FPA.DEC         STR     DEC
-        FPA.RADECSYS    STR     RADECSYS
+        FPA.RADECSYS    STR     EQUINOX
         FPA.OBSTYPE     STR     OBSTYPE
         FPA.OBJECT      STR     OBJECT
Index: /branches/cnb_branches/cnb_branch_20090301/ippconfig/gpc1/format_20081011.config
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippconfig/gpc1/format_20081011.config	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippconfig/gpc1/format_20081011.config	(revision 23352)
@@ -174,5 +174,5 @@
         FPA.RA          STR     RA
         FPA.DEC         STR     DEC
-        FPA.RADECSYS    STR     RADECSYS
+        FPA.RADECSYS    STR     EQUINOX
         FPA.OBSTYPE     STR     OBSTYPE
         FPA.OBJECT      STR     OBJECT
Index: /branches/cnb_branches/cnb_branch_20090301/ippconfig/gpc1/format_20090120.config
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippconfig/gpc1/format_20090120.config	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippconfig/gpc1/format_20090120.config	(revision 23352)
@@ -173,5 +173,5 @@
         FPA.RA          STR     RA
         FPA.DEC         STR     DEC
-        FPA.RADECSYS    STR     RADECSYS
+        FPA.RADECSYS    STR     EQUINOX
         FPA.OBSTYPE     STR     OBSTYPE
         FPA.OBJECT      STR     OBJECT
Index: /branches/cnb_branches/cnb_branch_20090301/ippconfig/gpc1/format_20090220.config
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippconfig/gpc1/format_20090220.config	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippconfig/gpc1/format_20090220.config	(revision 23352)
@@ -173,5 +173,5 @@
         FPA.RA          STR     RA
         FPA.DEC         STR     DEC
-        FPA.RADECSYS    STR     RADECSYS
+        FPA.RADECSYS    STR     EQUINOX
         FPA.OBSTYPE     STR     OBSTYPE
         FPA.OBJECT      STR     OBJECT
Index: /branches/cnb_branches/cnb_branch_20090301/ippconfig/gpc1/format_mef.config
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippconfig/gpc1/format_mef.config	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippconfig/gpc1/format_mef.config	(revision 23352)
@@ -168,5 +168,5 @@
         FPA.RA          STR     RA
         FPA.DEC         STR     DEC
-        FPA.RADECSYS    STR     RADECSYS
+        FPA.RADECSYS    STR     EQUINOX
 	FPA.OBSTYPE     STR     OBSTYPE
 	FPA.OBJECT	STR	OBJECT
Index: /branches/cnb_branches/cnb_branch_20090301/ippconfig/gpc1/format_orig.config
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippconfig/gpc1/format_orig.config	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippconfig/gpc1/format_orig.config	(revision 23352)
@@ -182,5 +182,5 @@
         # FPA.RA          STR     COMRA
         # FPA.DEC         STR     COMDEC
-        FPA.RADECSYS    STR     RADECSYS
+        FPA.RADECSYS    STR     EQUINOX
         FPA.OBSTYPE     STR     OBSTYPE
         FPA.OBJECT      STR     OBJECT
Index: /branches/cnb_branches/cnb_branch_20090301/ippconfig/gpc1/format_relphot.config
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippconfig/gpc1/format_relphot.config	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippconfig/gpc1/format_relphot.config	(revision 23352)
@@ -105,5 +105,5 @@
         FPA.RA          STR     RA
         FPA.DEC         STR     DEC
-        FPA.RADECSYS    STR     RADECSYS
+        FPA.RADECSYS    STR     EQUINOX
 	FPA.OBSTYPE     STR     OBSTYPE
 	FPA.OBJECT	STR	OBJECT
Index: /branches/cnb_branches/cnb_branch_20090301/ippconfig/gpc1/ppImage.config
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippconfig/gpc1/ppImage.config	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippconfig/gpc1/ppImage.config	(revision 23352)
@@ -10,4 +10,7 @@
 OVERSCAN.BOXCAR         S32     3
 
+# Normalization class
+NORM.CLASS              STR     CHIP             # How to find the per-class normalizations
+
 OLDDARK                 BOOL    FALSE
 
@@ -134,22 +137,50 @@
 # Overscan, bias, dark, shutter
 PPIMAGE_OBDS       METADATA
-  BASE.FITS        BOOL    TRUE            # Save base detrended image?
-  BASE.MASK.FITS   BOOL    TRUE            # Save base detrended image?
+  BASE.FITS          BOOL    TRUE            # Save base detrended image?
+  BASE.MASK.FITS     BOOL    TRUE            # Save base detrended image?
   BASE.VARIANCE.FITS BOOL    TRUE            # Save base detrended image?
-  CHIP.FITS        BOOL    FALSE           # Save chip-mosaic-ed image? 
-  CHIP.MASK.FITS   BOOL    FALSE           # Save chip-mosaic-ed image? 
-  CHIP.VARIANCE.FITS BOOL    FALSE           # Save chip-mosaic-ed image? 
-  OVERSCAN         BOOL    TRUE            # Overscan subtraction
-  BIAS             BOOL    FALSE           # Bias subtraction
-  DARK             BOOL    TRUE            # Dark subtraction
-  SHUTTER          BOOL    FALSE           # Shutter correction
-  FLAT             BOOL    FALSE           # Flat-field normalisation
-  MASK             BOOL    FALSE           # Mask bad pixels
-  FRINGE           BOOL    FALSE           # Fringe subtraction
-  PHOTOM           BOOL    FALSE           # Source identification and photometry
-  ASTROM.CHIP      BOOL    FALSE           # Astrometry per chip?
-  ASTROM.MOSAIC    BOOL    FALSE           # Astrometry for mosaic?
-  BIN1.FITS        BOOL    TRUE            # Save 1st binned chip image?
-  BIN2.FITS        BOOL    TRUE            # Save 2nd binned chip image?
+  CHIP.FITS          BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS     BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.VARIANCE.FITS BOOL    FALSE           # Save chip-mosaic-ed image? 
+  OVERSCAN           BOOL    TRUE            # Overscan subtraction
+  BIAS               BOOL    FALSE           # Bias subtraction
+  DARK               BOOL    TRUE            # Dark subtraction
+  SHUTTER            BOOL    FALSE           # Shutter correction
+  FLAT               BOOL    FALSE           # Flat-field normalisation
+  MASK               BOOL    FALSE           # Mask bad pixels
+  FRINGE             BOOL    FALSE           # Fringe subtraction
+  PHOTOM             BOOL    FALSE           # Source identification and photometry
+  ASTROM.CHIP        BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC      BOOL    FALSE           # Astrometry for mosaic?
+  BIN1.FITS          BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS          BOOL    TRUE            # Save 2nd binned chip image?
+END
+
+# Overscan, bias, dark, shutter
+PPIMAGE_FLATPROC_PREMASK       METADATA
+  BASE.FITS          BOOL    TRUE            # Save base detrended image?
+  BASE.MASK.FITS     BOOL    TRUE            # Save base detrended image?
+  BASE.VARIANCE.FITS BOOL    TRUE            # Save base detrended image?
+  CHIP.FITS          BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS     BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.VARIANCE.FITS BOOL    FALSE           # Save chip-mosaic-ed image? 
+  OVERSCAN           BOOL    TRUE            # Overscan subtraction
+  BIAS               BOOL    FALSE           # Bias subtraction
+  DARK               BOOL    TRUE            # Dark subtraction
+  SHUTTER            BOOL    FALSE           # Shutter correction
+  FLAT               BOOL    FALSE           # Flat-field normalisation
+  MASK               BOOL    FALSE           # Mask bad pixels
+  FRINGE             BOOL    FALSE           # Fringe subtraction
+  PHOTOM             BOOL    FALSE           # Source identification and photometry
+  ASTROM.CHIP        BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC      BOOL    FALSE           # Astrometry for mosaic?
+  BIN1.FITS          BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS          BOOL    TRUE            # Save 2nd binned chip image?
+
+  DETREND.CONSTRAINTS  METADATA
+    DARK METADATA
+      DETTYPE STR DARK_PREMASK
+    END
+  END   
 END
 
Index: /branches/cnb_branches/cnb_branch_20090301/ippconfig/gpc1/ppMerge.config
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippconfig/gpc1/ppMerge.config	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippconfig/gpc1/ppMerge.config	(revision 23352)
@@ -21,9 +21,9 @@
 DARK.ORDINATES	METADATA
 	CELL.DARKTIME	S32	1
-	CHIP.TEMP	METADATA
-		ORDER	S32	1
-		SCALE	BOOL	TRUE
-		MIN	F32	-95
-		MAX	F32	-50
-	END
+# 	CHIP.TEMP	METADATA
+# 		ORDER	S32	1
+# 		SCALE	BOOL	TRUE
+# 		MIN	F32	-95
+# 		MAX	F32	-50
+# 	END
 END
Index: /branches/cnb_branches/cnb_branch_20090301/ippconfig/ipprc.config.in
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippconfig/ipprc.config.in	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippconfig/ipprc.config.in	(revision 23352)
@@ -3,6 +3,7 @@
 # Default search path for configuration files (add $HOME if desired)
 # Note: do not include $HOME in the distributed copy used by ippMonitor
-# PATH            STR     @pkgdatadir@:$HOME/.ipp:.
-PATH              STR     $HOME/ippconfig/:$PSCONFDIR/$PSCONFIG/share/ippconfig/
+PATH            STR     @pkgdatadir@
+### This is a useful setting for an individual user:
+#PATH           STR     $HOME/ippconfig/:$PSCONFDIR/$PSCONFIG/share/ippconfig/
 
 # load the site-specific information from here
Index: /branches/cnb_branches/cnb_branch_20090301/ippconfig/megacam/psphot.config
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippconfig/megacam/psphot.config	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippconfig/megacam/psphot.config	(revision 23352)
@@ -14,5 +14,5 @@
 SKY_CLIP_SIGMA      F32  2.0             # statistic used to measure background
 
-PSF_SN_LIM          F32  100             # minimum S/N for stars used for PSF model
+PSF_SN_LIM          F32  20             # minimum S/N for stars used for PSF model
 PSF_MAX_NSTARS      S32  300             # limit number of stars used for PSF model
 
@@ -20,11 +20,10 @@
 PSF_MODEL           STR  PS_MODEL_QGAUSS
 
-MOMENTS_SN_MIN      F32   30.0
+PSF_MOMENTS_RADIUS  F32   10.0           # calculate initial source moments with this radius
+
+MOMENTS_SN_MIN      F32    2.0
 EXT_MIN_SN           F32  50.0           # fit galaxies above this S/N limit
 FULL_FIT_SN_LIM      F32  50.0
-AP_MIN_SN            F32  50.0
-
-# OUTPUT.FORMAT       STR SMPDATA
-OUTPUT.FORMAT       STR PS1_DEV_1
+AP_MIN_SN            F32   2.0
 
 PSF_SHAPE_NSIGMA     F32  3.0		 # max significance for shape variation
Index: /branches/cnb_branches/cnb_branch_20090301/ippconfig/recipes/filerules-mef.mdc
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippconfig/recipes/filerules-mef.mdc	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippconfig/recipes/filerules-mef.mdc	(revision 23352)
@@ -98,8 +98,9 @@
 PPSUB.INPUT.MASK        INPUT    @FILES        FPA        MASK
 PPSUB.INPUT.VARIANCE    INPUT    @FILES        FPA        VARIANCE
+PPSUB.INPUT.SOURCES     INPUT    @FILES        FPA        CMF
 PPSUB.REF               INPUT    @FILES        FPA        IMAGE
 PPSUB.REF.MASK          INPUT    @FILES        FPA        MASK
 PPSUB.REF.VARIANCE      INPUT    @FILES        FPA        VARIANCE
-PPSUB.SOURCES           INPUT    @FILES        FPA        CMF
+PPSUB.REF.SOURCES       INPUT    @FILES        FPA        CMF
 
 ## files used by ppstack
@@ -128,4 +129,5 @@
 PPIMAGE.OUT.WT.SPL      OUTPUT {OUTPUT}.{CHIP.NAME}.wt.fits      VARIANCE  NONE       CHIP       TRUE      SPLIT
 PPIMAGE.OUTPUT.DETMASK  OUTPUT {OUTPUT}.fits                     IMAGE     MASK       CHIP       TRUE      MEF
+PPIMAGE.OUTPUT.DETREND  OUTPUT {OUTPUT}.fits                     IMAGE     COMP_DET   CHIP       TRUE      MEF
 PPIMAGE.OUTPUT.RESID    OUTPUT {OUTPUT}.b0.fits                  IMAGE     COMP_SUB   CHIP       TRUE      MEF
 PPIMAGE.CONFIG          OUTPUT {OUTPUT}.{CHIP.NAME}.ppImage.mdc  TEXT      NONE       CHIP       TRUE      NONE
Index: /branches/cnb_branches/cnb_branch_20090301/ippconfig/recipes/filerules-simple.mdc
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippconfig/recipes/filerules-simple.mdc	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippconfig/recipes/filerules-simple.mdc	(revision 23352)
@@ -61,8 +61,9 @@
 PPSUB.INPUT.MASK        INPUT    @FILES        FPA        MASK
 PPSUB.INPUT.VARIANCE    INPUT    @FILES        FPA        VARIANCE
+PPSUB.INPUT.SOURCES     INPUT    @FILES        FPA        CMF
 PPSUB.REF               INPUT    @FILES        FPA        IMAGE
 PPSUB.REF.MASK          INPUT    @FILES        FPA        MASK
 PPSUB.REF.VARIANCE      INPUT    @FILES        FPA        VARIANCE
-PPSUB.SOURCES           INPUT    @FILES        FPA        CMF
+PPSUB.REF.SOURCES       INPUT    @FILES        FPA        CMF
 
 ## files used by ppstack
@@ -91,4 +92,5 @@
 PPIMAGE.OUTPUT.VARIANCE OUTPUT {OUTPUT}.wt.fits      VARIANCE  NONE       FPA        TRUE      SIMPLE
 PPIMAGE.OUTPUT.DETMASK  OUTPUT {OUTPUT}.fits         IMAGE     NONE       FPA        TRUE      SIMPLE
+PPIMAGE.OUTPUT.DETREND  OUTPUT {OUTPUT}.fits         IMAGE     NONE       FPA        TRUE      SIMPLE
 PPIMAGE.OUTPUT.RESID    OUTPUT {OUTPUT}.fits         IMAGE     NONE       FPA        TRUE      SIMPLE
 PPIMAGE.CONFIG          OUTPUT {OUTPUT}.ppImage.mdc  TEXT      NONE       FPA        TRUE      NONE
Index: /branches/cnb_branches/cnb_branch_20090301/ippconfig/recipes/filerules-split.mdc
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippconfig/recipes/filerules-split.mdc	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippconfig/recipes/filerules-split.mdc	(revision 23352)
@@ -73,8 +73,9 @@
 PPSUB.INPUT.MASK        INPUT    @FILES        FPA        MASK
 PPSUB.INPUT.VARIANCE    INPUT    @FILES        FPA        VARIANCE
+PPSUB.INPUT.SOURCES    	INPUT 	 @FILES        FPA        CMF
 PPSUB.REF               INPUT    @FILES        FPA        IMAGE
 PPSUB.REF.MASK          INPUT    @FILES        FPA        MASK
 PPSUB.REF.VARIANCE      INPUT    @FILES        FPA        VARIANCE
-PPSUB.SOURCES      	INPUT 	 @FILES        FPA        CMF
+PPSUB.REF.SOURCES    	INPUT 	 @FILES        FPA        CMF
 
 ## files used by ppstack
@@ -100,11 +101,7 @@
 PPIMAGE.OUTPUT.VARIANCE OUTPUT {OUTPUT}.{CHIP.NAME}.wt.fits      VARIANCE  COMP_WT    CHIP       TRUE      NONE
 PPIMAGE.OUTPUT.DETMASK  OUTPUT {OUTPUT}.{CHIP.NAME}.fits         IMAGE     DET_MASK   CHIP       TRUE      NONE
+PPIMAGE.OUTPUT.DETREND  OUTPUT {OUTPUT}.{CHIP.NAME}.fits         IMAGE     COMP_DET   CHIP       TRUE      NONE
 PPIMAGE.OUTPUT.RESID    OUTPUT {OUTPUT}.{CHIP.NAME}.fits         IMAGE     COMP_SUB   CHIP       TRUE      NONE
 PPIMAGE.CONFIG          OUTPUT {OUTPUT}.{CHIP.NAME}.ppImage.mdc  TEXT      NONE       CHIP       TRUE      NONE
-
-#PPIMAGE.OUTPUT          OUTPUT {OUTPUT}.{CHIP.NAME}.fits         IMAGE     NONE  CHIP       TRUE      NONE
-#PPIMAGE.OUTPUT.MASK     OUTPUT {OUTPUT}.{CHIP.NAME}.mk.fits      MASK      NONE  CHIP       TRUE      NONE
-#PPIMAGE.OUTPUT.VARIANCE OUTPUT {OUTPUT}.{CHIP.NAME}.wt.fits      VARIANCE  NONE  CHIP       TRUE      NONE
-#PPIMAGE.OUTPUT.DETMASK  OUTPUT {OUTPUT}.{CHIP.NAME}.fits         IMAGE     NONE  CHIP       TRUE      NONE
 	        									        
 PPIMAGE.CHIP            OUTPUT {OUTPUT}.{CHIP.NAME}.ch.fits      IMAGE     COMP_IMG   CHIP       TRUE      NONE
@@ -124,7 +121,7 @@
 		        									        
 PPIMAGE.STATS           OUTPUT {OUTPUT}.{CHIP.NAME}.stats        STATS     NONE       CHIP       TRUE      NONE
-		        									        
+
 ## note: these use the  same output naming convention since they are used for different ppMerge runs
-PPMERGE.OUTPUT.MASK     OUTPUT {OUTPUT}.{CHIP.NAME}.fits         MASK      NONE       CHIP       TRUE      NONE
+PPMERGE.OUTPUT.MASK     OUTPUT {OUTPUT}.{CHIP.NAME}.fits         MASK      DET_MASK   CHIP       TRUE      NONE
 PPMERGE.OUTPUT.BIAS     OUTPUT {OUTPUT}.{CHIP.NAME}.fits         IMAGE     NONE       CHIP       TRUE      NONE
 PPMERGE.OUTPUT.DARK     OUTPUT {OUTPUT}.{CHIP.NAME}.fits         DARK      NONE       CHIP       TRUE      NONE
Index: /branches/cnb_branches/cnb_branch_20090301/ippconfig/recipes/fitstypes.mdc
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippconfig/recipes/fitstypes.mdc	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippconfig/recipes/fitstypes.mdc	(revision 23352)
@@ -19,4 +19,17 @@
 # STDEV.NUM(F32) is the number of standard deviations to the edge (when SCALING = STDEV_NEGATIVE|STDEV_POSITIVE)
 # FLOAT(STR) is the name of a custom floating-point type
+
+# Compressed detrend
+COMP_FLAT	METADATA
+	BITPIX		S32	16
+	SCALING		STR	MANUAL
+	BSCALE		F32	1.0
+	BZERO		F32	32768.0
+	COMPRESSION	STR	RICE
+	TILE.X		S32	0
+	TILE.Y		S32	1
+	TILE.Z		S32	1
+	NOISE		S32	8
+END
 
 DET_IMAGE	METADATA
Index: /branches/cnb_branches/cnb_branch_20090301/ippconfig/recipes/jpeg.mdc
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippconfig/recipes/jpeg.mdc	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippconfig/recipes/jpeg.mdc	(revision 23352)
@@ -138,6 +138,6 @@
 		COLORMAP	STR	-greyscale
 		SCALE.MODE	STR	FRACTION
-		SCALE.MIN	F32	0.95
-		SCALE.MAX	F32	1.05
+		SCALE.MIN	F32	0.99
+		SCALE.MAX	F32	1.01
 	END
 
@@ -145,6 +145,6 @@
 		COLORMAP	STR	-greyscale
 		SCALE.MODE	STR	FRACTION
-		SCALE.MIN	F32	0.95
-		SCALE.MAX	F32	1.05
+		SCALE.MIN	F32	0.99
+		SCALE.MAX	F32	1.01
 	END
 END
Index: /branches/cnb_branches/cnb_branch_20090301/ippconfig/recipes/ppImage.config
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippconfig/recipes/ppImage.config	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippconfig/recipes/ppImage.config	(revision 23352)
@@ -41,4 +41,7 @@
 NONLIN.DATA             STR     nonlin.dat      # Filename for lookup table
 
+# Normalization class
+NORM.CLASS              STR     FPA             # How to find the per-class normalizations
+
 OLDDARK		BOOL	FALSE		# Use old-style darks?
 
@@ -433,4 +436,32 @@
   BIN1.FITS        BOOL    TRUE            # Save 1st binned chip image?
   BIN2.FITS        BOOL    TRUE            # Save 2nd binned chip image?
+END
+
+# used for the pre-mask version of flat-field processing
+PPIMAGE_FLATPROC_PREMASK       METADATA
+  BASE.FITS          BOOL    TRUE            # Save base detrended image?
+  BASE.MASK.FITS     BOOL    TRUE            # Save base detrended image?
+  BASE.VARIANCE.FITS BOOL    TRUE            # Save base detrended image?
+  CHIP.FITS          BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS     BOOL    FALSE           # Save chip-mosaic-ed image? 
+  CHIP.VARIANCE.FITS BOOL    FALSE           # Save chip-mosaic-ed image? 
+  OVERSCAN           BOOL    TRUE            # Overscan subtraction
+  BIAS               BOOL    TRUE            # Bias subtraction
+  DARK               BOOL    TRUE            # Dark subtraction
+  SHUTTER            BOOL    FALSE           # Shutter correction
+  FLAT               BOOL    FALSE           # Flat-field normalisation
+  MASK               BOOL    FALSE           # Mask bad pixels
+  FRINGE             BOOL    FALSE           # Fringe subtraction
+  PHOTOM             BOOL    FALSE           # Source identification and photometry
+  ASTROM.CHIP        BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC      BOOL    FALSE           # Astrometry for mosaic?
+  BIN1.FITS          BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS          BOOL    TRUE            # Save 2nd binned chip image?
+
+  DETREND.CONSTRAINTS  METADATA
+    DARK METADATA
+      DETTYPE STR DARK_PREMASK
+    END
+  END   
 END
 
Index: /branches/cnb_branches/cnb_branch_20090301/ippconfig/recipes/ppStack.config
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippconfig/recipes/ppStack.config	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippconfig/recipes/ppStack.config	(revision 23352)
@@ -32,12 +32,16 @@
 
 ZP.RADIUS	F32	1.0		# Radius (pixels) for matching sources
-ZP.ITER		S32	1000		# Maximum iterations for zero point
+ZP.ITER.1	S32	5		# Iterations for zero point; pass 1
+ZP.ITER.2	S32	1000		# Iterations for zero point; pass 2
 ZP.TOL		F32	1.0e-6		# Tolerance for zero point iterations
 ZP.TRANS.ITER	S32	2		# Iterations for transparency determination
 ZP.TRANS.REJ	F32	3.0		# Rejection threshold for transparency determination
 ZP.TRANS.THRESH	F32	1.0		# Threshold for transparency determination
-ZP.STAR.REJ	F32	3.0		# Rejection threshold for stars
+ZP.STAR.REJ.1	F32	20.0		# Rejection threshold for stars; pass 1
+ZP.STAR.REJ.2	F32	3.0		# Rejection threshold for stars; pass 2
 ZP.STAR.LIMIT	F32	1.0e-2		# Limit on star rejection fraction for successful iteration
-ZP.STAR.SYS	F32	0.05		# Estimated systematic error
+ZP.STAR.SYS.1	F32	0.10		# Estimated systematic error; pass 1
+ZP.STAR.SYS.2	F32	0.05		# Estimated systematic error; pass 2
+ZP.MATCH	F32	0.3		# Fraction of images to match for good star
 ZP.AIRMASS	METADATA		# Airmass terms by filter
 	g	F32	0.0
Index: /branches/cnb_branches/cnb_branch_20090301/ippconfig/recipes/ppSub.config
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippconfig/recipes/ppSub.config	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippconfig/recipes/ppSub.config	(revision 23352)
@@ -5,4 +5,5 @@
 SPATIAL.ORDER   S32	0		# Spatial polynomial order
 REGION.SIZE	F32	0		# Iso-kernel region size (pixels)
+SOURCE.RADIUS	F32	3.0		# Source matching radius (pixels)
 STAMP.SPACING   F32	200		# Typical spacing between stamps (pixels)
 STAMP.FOOTPRINT S32	20		# Size of stamps (pixels)
Index: /branches/cnb_branches/cnb_branch_20090301/ippconfig/recipes/psphot.config
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippconfig/recipes/psphot.config	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippconfig/recipes/psphot.config	(revision 23352)
@@ -16,5 +16,5 @@
 ZERO_PT                             F32   25.000          # zero point used by DVO
 
-OUTPUT.FORMAT                       STR   PS1_DEV_1
+OUTPUT.FORMAT                       STR   PS1_V1
 
 # these parameter govern how the background is measured
Index: /branches/cnb_branches/cnb_branch_20090301/ippconfig/recipes/reductionClasses.mdc
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippconfig/recipes/reductionClasses.mdc	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ippconfig/recipes/reductionClasses.mdc	(revision 23352)
@@ -31,10 +31,10 @@
 
 	# *** premask (unmasked) flat-field types
-	FLAT_PREMASK_PROCESS	STR	PPIMAGE_OBDS
+	FLAT_PREMASK_PROCESS	STR	PPIMAGE_FLATPROC_PREMASK
 	FLAT_PREMASK_RESID	STR	PPIMAGE_F
 	FLAT_PREMASK_VERIFY	STR	PPIMAGE_F
 	FLAT_PREMASK_STACK	STR	PPMERGE_FLAT
 	FLAT_PREMASK_JPEG_IMAGE	STR	FLAT
-	FLAT_PREMASK_JPEG_RESID	STRE	FLAT_RESID
+	FLAT_PREMASK_JPEG_RESID	STR	FLAT_RESID
 
 	DOMEFLAT_PREMASK_PROCESS	STR	PPIMAGE_OBDS
Index: /branches/cnb_branches/cnb_branch_20090301/magic/remove/src/streaksrelease.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/magic/remove/src/streaksrelease.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/magic/remove/src/streaksrelease.c	(revision 23352)
@@ -8,5 +8,4 @@
 static void excisePixel(streakFiles *sfiles, unsigned int x, unsigned int y, bool streak, double newMaskValue);
 static void writeImages(streakFiles *sf, bool exciseImageCube);
-static bool replicateOutputs(streakFiles *sfiles);
 
 int
@@ -252,4 +251,8 @@
         // image data directly from psFits
         readImage(sf->inImage, sf->extnum, sf->stage, false);
+        
+        // astrom struct is only needed for raw stage, and only the chip to cell parameters are used
+        sf->astrom = streakSetAstrometry(sf->astrom, sf->stage, NULL, NULL, false,
+            sf->inImage->header, sf->inImage->numCols, sf->inImage->numRows);
     }
     sf->outImage->header = (psMetadata*) psMemIncrRefCounter(sf->inImage->header);
@@ -264,6 +267,9 @@
     if (sf->inImage->image) {
         setupImageRefs(sf->outImage, sf->recImage, sf->inImage, sf->extnum, exciseAll);
+    } else if (sf->inImage->imagecube) {
+        // Image cubes should have been excised in the destreaking process
+        streaksExit("unexpected imagecube found", PS_EXIT_CONFIG_ERROR);
     }  else {
-        // Image cubes are handeled specially
+        return false;
     }
 
@@ -285,33 +291,35 @@
     if (sf->inMask) {
         readImage(sf->inMask, sf->extnum, sf->stage, true);
-        sf->outMask->header = (psMetadata*) psMemIncrRefCounter(sf->inMask->header);
-        if (sf->recMask) {
-            sf->recMask->header = (psMetadata*) psMemIncrRefCounter(sf->inMask->header);
-        }
-        setupImageRefs(sf->outMask, sf->recMask, sf->inMask, sf->extnum, exciseAll);
-        if (sf->outChMask) {
-            sf->outChMask->header = (psMetadata *) psMemIncrRefCounter(sf->outMask->header);
-            sf->outChMask->image = (psImage *) psMemIncrRefCounter(sf->outMask->image);
-            if (sf->recChMask) {
-                sf->recChMask->header = (psMetadata *) psMemIncrRefCounter(sf->recMask->header);
-                sf->recChMask->image = (psImage *) psMemIncrRefCounter(sf->recMask->image);
-            }
-        }
+        if (sf->outMask) {
+            sf->outMask->header = (psMetadata*) psMemIncrRefCounter(sf->inMask->header);
+            if (sf->recMask) {
+                sf->recMask->header = (psMetadata*) psMemIncrRefCounter(sf->inMask->header);
+            }
+            setupImageRefs(sf->outMask, sf->recMask, sf->inMask, sf->extnum, exciseAll);
+            if (sf->outChMask) {
+                sf->outChMask->header = (psMetadata *) psMemIncrRefCounter(sf->outMask->header);
+                sf->outChMask->image = (psImage *) psMemIncrRefCounter(sf->outMask->image);
+                if (sf->recChMask) {
+                    sf->recChMask->header = (psMetadata *) psMemIncrRefCounter(sf->recMask->header);
+                    sf->recChMask->image = (psImage *) psMemIncrRefCounter(sf->recMask->image);
+                }
+            }
 
 #ifdef STREAKS_COMPRESS_OUTPUT
-        // XXX: see note above
-        copyFitsOptions(sf->outMask, sf->recMask, sf->inMask);
-        psFitsSetCompression(sf->outMask->fits, PS_FITS_COMPRESS_PLIO, sf->tiles, 8, 0, 0);
-        if (sf->recMask) {
-            psFitsSetCompression(sf->recMask->fits, PS_FITS_COMPRESS_PLIO, sf->tiles, 8, 0, 0);
-        }
-        if (sf->outChMask) {
-            copyFitsOptions(sf->outChMask, sf->recChMask, sf->inMask);
-            psFitsSetCompression(sf->outChMask->fits, PS_FITS_COMPRESS_PLIO, sf->tiles, 8, 0, 0);
-            if (sf->recChMask) {
-                psFitsSetCompression(sf->recChMask->fits, PS_FITS_COMPRESS_PLIO, sf->tiles, 8, 0, 0);
-            }
-        }
+            // XXX: see note above
+            copyFitsOptions(sf->outMask, sf->recMask, sf->inMask);
+            psFitsSetCompression(sf->outMask->fits, PS_FITS_COMPRESS_PLIO, sf->tiles, 8, 0, 0);
+            if (sf->recMask) {
+                psFitsSetCompression(sf->recMask->fits, PS_FITS_COMPRESS_PLIO, sf->tiles, 8, 0, 0);
+            }
+            if (sf->outChMask) {
+                copyFitsOptions(sf->outChMask, sf->recChMask, sf->inMask);
+                psFitsSetCompression(sf->outChMask->fits, PS_FITS_COMPRESS_PLIO, sf->tiles, 8, 0, 0);
+                if (sf->recChMask) {
+                    psFitsSetCompression(sf->recChMask->fits, PS_FITS_COMPRESS_PLIO, sf->tiles, 8, 0, 0);
+                }
+            }
 #endif
+        }
     }
 
@@ -396,45 +404,2 @@
     }
 }
-
-// for any of the outputs that are stored in Nebulous set their extended attributes
-// and replicate the files
-
-static bool
-replicateOutputs(streakFiles *sfiles)
-{
-    bool status = false;
-
-    // XXX: TODO: need a nebGetXatrr function, but there isn't one
-    // another option would be to take the number of copies to be
-    // created as an option. That way the system could decide
-    // whether to replicate anything other than raw Image files
-    void *xattr = NULL;
-
-    if (!replicate(sfiles->outImage, xattr)) {
-        psError(PM_ERR_SYS, false, "failed to replicate outImage.");
-        return false;
-    }
-
-#ifdef notyet
-    // XXX: don't replicate mask and weight images until we can look up
-    // the input's xattr. There may be a perl program that can getXattr
-    if (sfiles->outMask) {
-        // get xattr from input to see if we need to replicate
-        if (!replicate(sfiles->outMask, xattr)) {
-            psError(PM_ERR_SYS, false, "failed to replicate outImage.");
-            return false;
-        }
-    }
-    if (sfiles->outWeight) {
-        // get xattr from input to see if we need to replicate
-        if (!replicate(sfiles->outWeight, xattr)) {
-            psError(PM_ERR_SYS, false, "failed to replicate outImage.");
-            return false;
-        }
-    }
-#endif
-
-    return true;
-}
-
-
Index: /branches/cnb_branches/cnb_branch_20090301/magic/remove/src/streaksreplace.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/magic/remove/src/streaksreplace.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/magic/remove/src/streaksreplace.c	(revision 23352)
@@ -4,5 +4,4 @@
 static bool readAndCopyToOutput(streakFiles *sf, bool restoreImageCube);
 static void writeImages(streakFiles *sf, bool exciseImageCube);
-static bool replicateOutputs(streakFiles *sfiles);
 static bool readAndCopyToOutput(streakFiles *sf, bool restoreImageCube);
 
@@ -92,7 +91,4 @@
     closeImages(sfiles);
 
-    // NOTE: from here on we can't just quit if something goes wrong.
-    // especially if we're working at the raw stage
-
     if (!replicateOutputs(sfiles)) {
         psError(PS_ERR_UNKNOWN, false, "failed to replicate output files");
@@ -429,44 +425,2 @@
     }
 }
-
-static bool replicateOutputs(streakFiles *sfiles)
-{
-    bool status = false;
-
-    // XXX: TODO: need a nebGetXatrr function, but there isn't one
-    // another option would be to take the number of copies to be
-    // created as an option. That way the system could decide
-    // whether to replicate anything other than raw Image files
-    void *xattr = NULL;
-
-    if (!replicate(sfiles->outImage, xattr)) {
-        psError(PM_ERR_SYS, false, "failed to replicate outImage.");
-        return false;
-    }
-
-#ifdef notyet
-    // XXX: don't replicate mask and weight images until we can look up
-    // the input's xattr. There may be a perl program that can getXattr
-    if (sfiles->outMask) {
-        // get xattr from input to see if we need to replicate
-        if (!replicate(sfiles->outMask, xattr)) {
-            psError(PM_ERR_SYS, false, "failed to replicate outImage.");
-            return false;
-        }
-    }
-    if (sfiles->outWeight) {
-        // get xattr from input to see if we need to replicate
-        if (!replicate(sfiles->outWeight, xattr)) {
-            psError(PM_ERR_SYS, false, "failed to replicate outImage.");
-            return false;
-        }
-    }
-#endif
-
-//      replicate the recovery images (if in nebulous)
-//      perhaps whether we do that or not should be configurable.
-//      Sounds like we need a recipe
-
-    return true;
-}
-
Index: /branches/cnb_branches/cnb_branch_20090301/ppArith/src/Makefile.am
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppArith/src/Makefile.am	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ppArith/src/Makefile.am	(revision 23352)
@@ -1,4 +1,14 @@
 bin_PROGRAMS = ppArith
-ppArith_CPPFLAGS = $(PSLIB_CFLAGS) $(PSMODULE_CFLAGS) $(PPSTATS_CFLAGS) $(PSPHOT_CFLAGS) $(PPARITH_CFLAGS)
+
+# PPARITH_VERSION=`if [ -e ../../VERSION ]; then cat ../../VERSION; else svnversion; fi`
+# PPARITH_BRANCH=`if [ -e ../../BRANCH ]; then cat ../../BRANCH; else svn info | sed -n -e '/URL:/ h' -e '/Repository Root:/ { x; H; x; s|Repository Root: \(.*\)\nURL: \1\(.*\)|\2| ; s|^/|| ; s|/[a-zA-Z]*/src.*|| ; p }'; fi`
+# PPARITH_SOURCE=`if [ -e ../../SOURCE ]; then cat ../../SOURCE; else svn info | sed -n -e 's/Repository UUID: // p'; fi`
+# 
+# # Force recompilation of ppArithVersion.c, since it gets the version information
+# ppArithVersion.c: FORCE
+# 	touch ppArith.c
+# FORCE: ;
+
+ppArith_CPPFLAGS = $(PSLIB_CFLAGS) $(PSMODULE_CFLAGS) $(PPSTATS_CFLAGS) $(PSPHOT_CFLAGS) $(PPARITH_CFLAGS) -DPPARITH_VERSION=$(SVN_VERSION) -DPPARITH_BRANCH=$(SVN_BRANCH) -DPPARITH_SOURCE=$(SVN_SOURCE)
 ppArith_LDFLAGS  = $(PSLIB_LIBS)   $(PSMODULE_LIBS)   $(PPSTATS_LIBS)   $(PSPHOT_LIBS)   $(PPARITH_LIBS)
 
Index: /branches/cnb_branches/cnb_branch_20090301/ppArith/src/ppArith.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppArith/src/ppArith.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ppArith/src/ppArith.c	(revision 23352)
@@ -34,4 +34,6 @@
     }
 
+    ppArithVersionPrint();
+
     if (!ppArithArguments(argc, argv, config)) {
         psErrorStackPrint(stderr, "Error reading arguments.\n");
Index: /branches/cnb_branches/cnb_branch_20090301/ppArith/src/ppArith.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppArith/src/ppArith.h	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ppArith/src/ppArith.h	(revision 23352)
@@ -22,6 +22,6 @@
  * Parse the arguments
  */
-bool ppArithArguments(int argc, char *argv[], ///< Command-line arguments 
-                      pmConfig *config    ///< Configuration 
+bool ppArithArguments(int argc, char *argv[], ///< Command-line arguments
+                      pmConfig *config    ///< Configuration
     );
 
@@ -29,5 +29,5 @@
  * Parse the camera input
  */
-bool ppArithCamera(pmConfig *config       ///< Configuration 
+bool ppArithCamera(pmConfig *config       ///< Configuration
     );
 
@@ -35,5 +35,5 @@
  * Loop over the FPA hierarchy
  */
-bool ppArithLoop(pmConfig *config         ///< Configuration 
+bool ppArithLoop(pmConfig *config         ///< Configuration
     );
 
@@ -41,16 +41,19 @@
  * Perform arithmetic on the readout
  */
-bool ppArithReadout(pmReadout *output,  ///< Output readout 
-                    const pmReadout *input1, ///< Input readout 
-                    const pmReadout *input2, ///< Input readout 
-                    const pmConfig *config, ///< Configuration 
-                    const pmFPAview *view ///< View of readout on which to operate 
+bool ppArithReadout(pmReadout *output,  ///< Output readout
+                    const pmReadout *input1, ///< Input readout
+                    const pmReadout *input2, ///< Input readout
+                    const pmConfig *config, ///< Configuration
+                    const pmFPAview *view ///< View of readout on which to operate
     );
 
 /**
- * Put the program version information into a metadata
+ * Put the program version information into header
  */
-void ppArithVersionMetadata(psMetadata *metadata ///< Metadata to populate
+bool ppArithVersionHeader(psMetadata *header ///< Header to populate
     );
+
+/// Print version information
+void ppArithVersionPrint(void);
 
 ///@}
Index: /branches/cnb_branches/cnb_branch_20090301/ppArith/src/ppArithLoop.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppArith/src/ppArithLoop.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ppArith/src/ppArithLoop.c	(revision 23352)
@@ -113,5 +113,5 @@
                     hdu->header = psMetadataAlloc();
                 }
-                ppArithVersionMetadata(hdu->header);
+                ppArithVersionHeader(hdu->header);
                 lastHDU = hdu;
             }
Index: /branches/cnb_branches/cnb_branch_20090301/ppArith/src/ppArithVersion.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppArith/src/ppArithVersion.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ppArith/src/ppArithVersion.c	(revision 23352)
@@ -22,26 +22,86 @@
 #include "ppArith.h"
 
-static const char *cvsTag = "$Name: not supported by cvs2svn $";///< CVS tag name
+#ifndef PPARITH_VERSION
+#error "PPARITH_VERSION is not set"
+#endif
+#ifndef PPARITH_BRANCH
+#error "PPARITH_BRANCH is not set"
+#endif
+#ifndef PPARITH_SOURCE
+#error "PPARITH_SOURCE is not set"
+#endif
+
+#define xstr(s) str(s)
+#define str(s) #s
 
 psString ppArithVersion(void)
 {
-    psString version = NULL;            // Version, to return
-    psStringAppend(&version, "%s-%s",PACKAGE_NAME,PACKAGE_VERSION);
-    return version;
+    char *value = NULL;
+    psStringAppend(&value, "%s@%s", xstr(PPARITH_BRANCH), xstr(PPARITH_VERSION));
+    return value;
+}
+
+psString ppArithSource(void)
+{
+    return psStringCopy(xstr(PPARITH_SOURCE));
 }
 
 psString ppArithVersionLong(void)
 {
-    psString version = ppArithVersion(); // Version, to return
-    psString tag = psStringStripCVS(cvsTag, "Name"); // CVS tag
-    psStringAppend(&version, " (cvs tag %s) %s, %s", tag, __DATE__, __TIME__);
-    psFree(tag);
+    psString version = ppArithVersion();  // Version, to return
+    psString source = ppArithSource();    // Source
+
+    psStringPrepend(&version, "ppArith ");
+    psStringAppend(&version, " from %s, built %s, %s", source, __DATE__, __TIME__);
+    psFree(source);
+
+#ifdef __OPTIMIZE__
+    psStringAppend(&version, " optimised");
+#else
+    psStringAppend(&version, " unoptimised");
+#endif
+
     return version;
+};
+
+bool ppArithVersionHeader(psMetadata *header)
+{
+    PS_ASSERT_METADATA_NON_NULL(header, false);
+
+    psTime *time = psTimeGetNow(PS_TIME_TAI); // The time now
+    psString timeString = psTimeToISO(time); // The time in an ISO string
+    psFree(time);
+    psString history = NULL;               // History string
+    psStringAppend(&history, "ppArith at %s", timeString);
+    psFree(timeString);
+    psMetadataAddStr(header, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, NULL, history);
+    psFree(history);
+
+    psLibVersionHeader(header);
+    psModulesVersionHeader(header);
+    ppStatsVersionHeader(header);
+
+    psString version = ppArithVersion(); // Software version
+    psString source  = ppArithSource();  // Software source
+
+    psStringPrepend(&version, "ppArith version: ");
+    psStringPrepend(&version, "ppArith source: ");
+
+    psMetadataAddStr(header, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, NULL, version);
+    psMetadataAddStr(header, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, NULL, source);
+
+    psFree(version);
+    psFree(source);
+
+    return true;
 }
 
-
-void ppArithVersionMetadata(psMetadata *metadata)
+void ppArithVersionPrint(void)
 {
-    PS_ASSERT_METADATA_NON_NULL(metadata,);
+    psTime *time = psTimeGetNow(PS_TIME_TAI); // The time now
+    psString timeString = psTimeToISO(time); // The time in an ISO string
+    psFree(time);
+    psLogMsg("ppArith", PS_LOG_INFO, "ppArith at %s", timeString);
+    psFree(timeString);
 
     psString pslib = psLibVersionLong();// psLib version
@@ -50,18 +110,9 @@
     psString ppArith = ppArithVersionLong(); // ppArith version
 
-    psTime *time = psTimeGetNow(PS_TIME_TAI); // The time now
-    psString timeString = psTimeToISO(time); // The time in an ISO string
-    psFree(time);
-    psString head = NULL;               // Head string
-    psStringAppend(&head, "ppArith processing at %s. Component information:", timeString);
-    psFree(timeString);
+    psLogMsg("ppArith", PS_LOG_INFO, "%s", pslib);
+    psLogMsg("ppArith", PS_LOG_INFO, "%s", psmodules);
+    psLogMsg("ppArith", PS_LOG_INFO, "%s", ppStats);
+    psLogMsg("ppArith", PS_LOG_INFO, "%s", ppArith);
 
-    psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, head, "");
-    psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, pslib, "");
-    psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, psmodules, "");
-    psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, ppStats, "");
-    psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, ppArith, "");
-
-    psFree(head);
     psFree(pslib);
     psFree(psmodules);
Index: /branches/cnb_branches/cnb_branch_20090301/ppImage/src/Makefile.am
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppImage/src/Makefile.am	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ppImage/src/Makefile.am	(revision 23352)
@@ -4,5 +4,14 @@
 	ppImage.h 
 
-ppImage_CFLAGS = $(PPIMAGE_CFLAGS) $(PPSTATS_CFLAGS) $(PSASTRO_CFLAGS) $(PSPHOT_CFLAGS) $(PSMODULE_CFLAGS) $(PSLIB_CFLAGS)
+#PPIMAGE_VERSION=`if [ -e ../../VERSION ]; then cat ../../VERSION; else svnversion; fi`
+#PPIMAGE_BRANCH=`if [ -e ../../BRANCH ]; then cat ../../BRANCH; else svn info | sed -n -e '/URL:/ h' -e '/Repository Root:/ { x; H; x; s|Repository Root: \(.*\)\nURL: \1\(.*\)|\2| ; s|^/|| ; s|/[a-zA-Z]*/src.*|| ; p }'; fi`
+#PPIMAGE_SOURCE=`if [ -e ../../SOURCE ]; then cat ../../SOURCE; else svn info | sed -n -e 's/Repository UUID: // p'; fi`
+
+# Force recompilation of ppImageVersion.c, since it gets the version information
+# ppImageVersion.c: FORCE
+# 	touch ppImageVersion.c
+# FORCE: ;
+
+ppImage_CFLAGS = $(PPIMAGE_CFLAGS) $(PPSTATS_CFLAGS) $(PSASTRO_CFLAGS) $(PSPHOT_CFLAGS) $(PSMODULE_CFLAGS) $(PSLIB_CFLAGS) -DPPIMAGE_VERSION=$(SVN_VERSION) -DPPIMAGE_BRANCH=$(SVN_BRANCH) -DPPIMAGE_SOURCE=$(SVN_SOURCE)
 ppImage_LDFLAGS = $(PPIMAGE_LIBS) $(PSASTRO_LIBS) $(PPSTATS_LIBS) $(PSPHOT_LIBS) $(PSMODULE_LIBS) $(PSLIB_LIBS)
 ppImage_SOURCES = \
Index: /branches/cnb_branches/cnb_branch_20090301/ppImage/src/ppImage.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppImage/src/ppImage.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ppImage/src/ppImage.c	(revision 23352)
@@ -21,4 +21,6 @@
         exit(PS_EXIT_CONFIG_ERROR);
     }
+
+    ppImageVersionPrint();
 
     // define recipe options
Index: /branches/cnb_branches/cnb_branch_20090301/ppImage/src/ppImage.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppImage/src/ppImage.h	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ppImage/src/ppImage.h	(revision 23352)
@@ -86,4 +86,6 @@
     int remnanceSize;                   // Size for remnance detection
     float remnanceThresh;               // Threshold for remnance detection
+
+    char *normClass;			// class to use for per-class normalization 
 } ppImageOptions;
 
@@ -208,10 +210,16 @@
 psString ppImageVersion(void);
 
+/// Return software source
+psString ppImageSource(void);
+
 /// Return long version information
 psString ppImageVersionLong(void);
 
-/// Update the metadata with version information for all dependencies
-void ppImageVersionMetadata(psMetadata *metadata ///< Metadata to update with version information
-    );
+/// Populate the header with version information for all dependencies
+bool ppImageVersionHeader(psMetadata *metadata ///< Header to populate
+    );
+
+/// Print version information
+void ppImageVersionPrint(void);
 
 
Index: /branches/cnb_branches/cnb_branch_20090301/ppImage/src/ppImageArguments.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppImage/src/ppImageArguments.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ppImage/src/ppImageArguments.c	(revision 23352)
@@ -14,4 +14,5 @@
     fprintf(stderr, "\t-chip CHIPNUM: Only process this chip number.\n");
     fprintf(stderr, "\t-norm VALUE: Divide through by this value when done.\n");
+    fprintf(stderr, "\t-normlist file.mdc: normalizations by class_id.\n");
     fprintf(stderr, "\n");
     fprintf(stderr, "Input options (single file / file list):\n");
@@ -80,5 +81,5 @@
     if ((argnum = psArgumentGet(argc, argv, "-visual"))) {
         psArgumentRemove(argnum, &argc, argv);
-	psphotSetVisual (true);
+        pmVisualSetVisual(true);
     }
 
@@ -86,11 +87,11 @@
     if ((argnum = psArgumentGet(argc, argv, "-threads"))) {
         psArgumentRemove(argnum, &argc, argv);
-	int nThreads = atoi(argv[argnum]);
+        int nThreads = atoi(argv[argnum]);
         psMetadataAddS32(config->arguments, PS_LIST_TAIL, "NTHREADS", 0, "number of warp threads", nThreads);
         psArgumentRemove(argnum, &argc, argv);
 
-	// create the thread pool with number of desired threads, supplying our thread launcher function
-	// XXX need to determine the number of threads from the config data
-	psThreadPoolInit (nThreads);
+        // create the thread pool with number of desired threads, supplying our thread launcher function
+        // XXX need to determine the number of threads from the config data
+        psThreadPoolInit (nThreads);
     }
 
@@ -107,8 +108,5 @@
 
     // the input file is a required argument; if not found, we will exit
-    bool status = pmConfigFileSetsMD (config->arguments, &argc, argv, "INPUT", "-file", "-list");
-    if (!status) {
-        usage ();
-    }
+    pmConfigFileSetsMD (config->arguments, &argc, argv, "INPUT", "-file", "-list");
 
     // if these command-line options are supplied, load the file name lists into config->arguments
@@ -128,10 +126,23 @@
     }
 
-    // Optional normalisation factor
+    // Optional normalization factor
     if ((argnum = psArgumentGet(argc, argv, "-norm"))) {
         psArgumentRemove(argnum, &argc, argv);
         float norm = atof(argv[argnum]);
-        psMetadataAddF32(config->arguments, PS_LIST_TAIL, "NORMALISATION", 0,
+        psMetadataAddF32(config->arguments, PS_LIST_TAIL, "NORMALIZATION", 0,
                          "Normalisation to apply", norm);
+        psArgumentRemove(argnum, &argc, argv);
+    }
+
+    // Optional per-class normalization table
+    if ((argnum = psArgumentGet(argc, argv, "-normlist"))) {
+        psArgumentRemove(argnum, &argc, argv);
+
+        unsigned int nFail = 0;
+        psMetadata *normlist = psMetadataConfigRead (NULL, &nFail, argv[argnum], false);
+        // XXX allow this file to be in nebulous?
+
+        psMetadataAddMetadata(config->arguments, PS_LIST_TAIL, "NORMALIZATION.TABLE", 0, "Normalization to apply", normlist);
+        psFree (normlist);
         psArgumentRemove(argnum, &argc, argv);
     }
Index: /branches/cnb_branches/cnb_branch_20090301/ppImage/src/ppImageDefineFile.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppImage/src/ppImageDefineFile.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ppImage/src/ppImageDefineFile.c	(revision 23352)
@@ -5,51 +5,52 @@
 # include "ppImage.h"
 
-bool ppImageDefineFile (pmConfig *config, pmFPA *input, char *filerule, char *argname, pmFPAfileType fileType, pmDetrendType detrendType) {
+bool ppImageDefineFile(pmConfig *config, pmFPA *input, char *filerule, char *argname,
+                       pmFPAfileType fileType, pmDetrendType detrendType)
+{
+    bool status;
+    pmFPAfile *file = NULL;             // File to be defined
 
-    bool status;
-    pmFPAfile *file;
-
-    // look for the file on the argument list
-    file = pmFPAfileDefineFromArgs  (&status, config, filerule, argname);
-    if (!status) {
-	psError (PS_ERR_UNKNOWN, false, "failed to load find definition");
-	return false;
+    if (!file) {
+        // look for the file on the RUN metadata
+        file = pmFPAfileDefineFromRun(&status, config, filerule);
+        if (!status) {
+            psError(PS_ERR_UNKNOWN, false, "failed to load file definition");
+            return false;
+        }
     }
-    if (file) {
-	if (file->type != fileType) {
-	    psError(PS_ERR_IO, true, "%s is not of type %s", filerule, pmFPAfileStringFromType (fileType));
-	    return false;
-	}
-	return true;
+    if (!file) {
+        // look for the file on the argument list
+        file = pmFPAfileDefineFromArgs(&status, config, filerule, argname);
+        if (!status) {
+            psError(PS_ERR_UNKNOWN, false, "failed to load file definition");
+            return false;
+        }
+    }
+    if (!file) {
+        // look for the file in the camera config table
+        file = pmFPAfileDefineFromConf(&status, config, filerule);
+        if (!status) {
+            psError(PS_ERR_UNKNOWN, false, "failed to load file definition");
+            return false;
+        }
+    }
+    if (!file) {
+        // look for the file to be loaded from the detrend database
+        file = pmFPAfileDefineFromDetDB(&status, config, filerule, input, detrendType);
+        if (!status) {
+            psError(PS_ERR_UNKNOWN, false, "failed to load file definition");
+            return false;
+        }
     }
 
-    // look for the file in the camera config table
-    file = pmFPAfileDefineFromConf  (&status, config, filerule);
-    if (!status) {
-	psError (PS_ERR_UNKNOWN, false, "failed to load find definition");
-	return false;
-    }
-    if (file) {
-	if (file->type != fileType) {
-	    psError(PS_ERR_IO, true, "%s is not of type %s", filerule, pmFPAfileStringFromType (fileType));
-	    return false;
-	}
-	return true;
+    if (!file) {
+        return false;
     }
 
-    // look for the file to be loaded from the detrend database
-    file = pmFPAfileDefineFromDetDB (&status, config, filerule, input, detrendType);
-    if (!status) {
-	psError (PS_ERR_UNKNOWN, false, "failed to load file definition");
-	return false;
+    if (file->type != fileType) {
+        psError(PS_ERR_IO, true, "%s is not of type %s", filerule, pmFPAfileStringFromType(fileType));
+        return false;
     }
-    if (file) {
-	if (file->type != fileType) {
-	    psError(PS_ERR_IO, true, "%s is not of type %s", filerule, pmFPAfileStringFromType (fileType));
-	    return false;
-	}
-	return true;
-    }
-    return false;
+    return true;
 }
 
Index: /branches/cnb_branches/cnb_branch_20090301/ppImage/src/ppImageDetrendReadout.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppImage/src/ppImageDetrendReadout.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ppImage/src/ppImageDetrendReadout.c	(revision 23352)
@@ -54,4 +54,5 @@
         if (!pmBiasSubtract(input, options->overscan, bias, oldDark, view)) {
             psError(PS_ERR_UNKNOWN, false, "Unable to subtract bias.");
+	    psFree(detview);
             return false;
         }
@@ -67,4 +68,5 @@
         if (!pmDarkApply(input, dark, options->maskValue)) {
             psError(PS_ERR_UNKNOWN, false, "Unable to subtract dark.");
+	    psFree(detview);
             return false;
         }
@@ -75,4 +77,5 @@
                         options->remnanceSize, options->remnanceThresh)) {
             psError(PS_ERR_UNKNOWN, false, "Unable to mask remnance.");
+	    psFree(detview);
             return false;
         }
@@ -83,4 +86,5 @@
         pmReadout *shutter = pmFPAfileThisReadout(config->files, detview, "PPIMAGE.SHUTTER");
         if (!pmShutterCorrectionApply(input, shutter, pmConfigMaskGet("FLAT", config))) {
+	    psFree(detview);
             return false;
         }
@@ -91,11 +95,12 @@
         pmReadout *flat = pmFPAfileThisReadout(config->files, detview, "PPIMAGE.FLAT");
         if (!pmFlatField(input, flat, options->flatMask)) {
+	    psFree(detview);
             return false;
         }
     }
 
-    // Normalisation by a (known) constant
+    // Normalization by a single (known) constant
     bool mdok;                          // Status of MD lookup
-    float norm = psMetadataLookupF32(&mdok, config->arguments, "NORMALISATION");
+    float norm = psMetadataLookupF32(&mdok, config->arguments, "NORMALIZATION");
     if (mdok && isfinite(norm) && norm != 1.0) {
         pmHDU *hdu = pmHDUFromReadout(input); // HDU of interest
@@ -108,7 +113,52 @@
     }
 
+# if (1)
+    // Normalization by per-class values
+    psMetadata *normlist = psMetadataLookupMetadata(&mdok, config->arguments, "NORMALIZATION.TABLE");
+    if (normlist) {
+	pmFPAfile *inputFile = psMetadataLookupPtr(&mdok, config->files, "PPIMAGE.INPUT");
+
+	// get the menu of class IDs
+        psMetadata *menu = psMetadataLookupMetadata(&mdok, inputFile->camera, "CLASSID"); 
+        if (!menu) {
+            psError(PS_ERR_IO, false, "Unable to find CLASSID metadata in camera configuration");
+	    psFree(detview);
+            return false;
+        }
+	// get the rule for class_id for the desired class
+        const char *rule = psMetadataLookupStr(&mdok, menu, options->normClass); 
+        if (!rule) {
+            psError(PS_ERR_IO, false, "Unable to find NORM.CLASS value %s in CLASSID in camera configuration", options->normClass);
+	    psFree(detview);
+            return false;
+        }
+	// get the class_id from the rule
+        char *classID = pmFPAfileNameFromRule(rule, inputFile, view);
+        if (!classID) {
+            psError(PS_ERR_IO, false, "error converting CLASSID rule %s to name\n", rule);
+	    psFree(detview);
+            return false;
+        }
+
+	// get normalization from the class_id
+	float norm = psMetadataLookupF32 (&mdok, normlist, classID);
+
+        pmHDU *hdu = pmHDUFromReadout(input); // HDU of interest
+        psString comment = NULL;        // Comment to add
+        psStringAppend(&comment, "Normalization: %f", norm);
+        psMetadataAddStr(hdu->header, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, comment, "");
+        psFree(comment);
+
+	// apply the normalization
+        psBinaryOp(input->image, input->image, "*", psScalarAlloc(norm, PS_TYPE_F32));
+
+	psFree (classID);
+    }
+# endif
+
     if (options->doFringe) {
         pmCell *fringe = pmFPAfileThisCell(config->files, detview, "PPIMAGE.FRINGE");
         if (!ppImageDetrendFringeMeasure(input, fringe, false, options)) {
+	    psFree(detview);
             return false;
         }
Index: /branches/cnb_branches/cnb_branch_20090301/ppImage/src/ppImageLoop.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppImage/src/ppImageLoop.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ppImage/src/ppImageLoop.c	(revision 23352)
@@ -25,11 +25,6 @@
     }
 
-    psString dump_file = psMetadataLookupStr(&status, config->arguments, "DUMP_CONFIG");
-    if (dump_file) {
-        pmConfigCamerasCull(config, NULL);
-        pmConfigRecipesCull(config, "PPIMAGE,PPSTATS,PSPHOT,MASKS,PSASTRO");
-
-        pmConfigDump(config, input->fpa, dump_file);
-    }
+    pmConfigCamerasCull(config, NULL);
+    pmConfigRecipesCull(config, "PPIMAGE,PPSTATS,PSPHOT,MASKS,PSASTRO,JPEG");
 
     pmFPAview *view = pmFPAviewAlloc(0);// View for level of interest
@@ -63,7 +58,7 @@
 
             // Put version information into the header
-            pmHDU *hdu = pmHDUFromCell(cell);
+            pmHDU *hdu = pmHDUGetHighest(input->fpa, chip, cell);
             if (hdu && hdu != lastHDU) {
-                ppImageVersionMetadata(hdu->header);
+                ppImageVersionHeader(hdu->header);
                 lastHDU = hdu;
             }
@@ -96,8 +91,8 @@
                 }
 
-		// free detrend images potentially in use: MASK, BIAS, DARK, SHUTTER, FLAT
-		if (!ppImageDetrendFree (config, view)) {
-		    ESCAPE("Unable to free detrend images");
-		}
+                // free detrend images potentially in use: MASK, BIAS, DARK, SHUTTER, FLAT
+                if (!ppImageDetrendFree (config, view)) {
+                    ESCAPE("Unable to free detrend images");
+                }
             }
 
@@ -105,13 +100,13 @@
                 ppImageDetrendRecord(cell, config, options, view);
             }
-	    // free detrend images potentially in use: MASK, BIAS, DARK, SHUTTER, FLAT
-	    if (!ppImageDetrendFree (config, view)) {
-		ESCAPE("Unable to free detrend images");
-	    }
-        }
-	// free detrend images potentially in use: MASK, BIAS, DARK, SHUTTER, FLAT
-	if (!ppImageDetrendFree (config, view)) {
-	    ESCAPE("Unable to free detrend images");
-	}
+            // free detrend images potentially in use: MASK, BIAS, DARK, SHUTTER, FLAT
+            if (!ppImageDetrendFree (config, view)) {
+                ESCAPE("Unable to free detrend images");
+            }
+        }
+        // free detrend images potentially in use: MASK, BIAS, DARK, SHUTTER, FLAT
+        if (!ppImageDetrendFree (config, view)) {
+            ESCAPE("Unable to free detrend images");
+        }
 
         // Apply the fringe correction
@@ -121,8 +116,8 @@
             }
         }
-	// free detrend images potentially in use: MASK, BIAS, DARK, SHUTTER, FLAT
-	if (!ppImageFringeFree (config, view)) {
-	    ESCAPE("Unable to free fringe images");
-	}
+        // free detrend images potentially in use: MASK, BIAS, DARK, SHUTTER, FLAT
+        if (!ppImageFringeFree (config, view)) {
+            ESCAPE("Unable to free fringe images");
+        }
 
         // measure various pixel-based statistics for this image
@@ -152,15 +147,15 @@
         }
 
-	// these may be used by ppImageSubtractBackground.
-	// if these are defined as internal files, drop them here
-	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;
-	}
+        // these may be used by ppImageSubtractBackground.
+        // if these are defined as internal files, drop them here
+        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;
+        }
 
         // binning (used for display) must take place after the background is replaced, if desired
@@ -219,4 +214,10 @@
     }
     psFree(view);
+
+    // Dump configuration
+    psString dump_file = psMetadataLookupStr(&status, config->arguments, "DUMP_CONFIG");
+    if (dump_file) {
+        pmConfigDump(config, input->fpa, dump_file);
+    }
 
     // Write out summary statistics
Index: anches/cnb_branches/cnb_branch_20090301/ppImage/src/ppImageLoop_Threaded.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppImage/src/ppImageLoop_Threaded.c	(revision 23351)
+++ 	(revision )
@@ -1,141 +1,0 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include "ppImage.h"
-
-# define ESCAPE(MESSAGE) { \
-  psError(PS_ERR_UNKNOWN, false, MESSAGE); \
-  psFree (view); \
-  return false; \
-}
-
-bool ppImageLoop (pmConfig *config, ppImageOptions *options) {
-
-    bool status;
-    pmChip *chip;
-    pmCell *cell;
-    pmReadout *readout;
-
-    // measure the total elapsed time in ppImageLoop.
-    psTimerStart ("ppImageLoop");
-
-    pmFPAfile *input = psMetadataLookupPtr(&status, config->files, "PPIMAGE.INPUT");
-    if (!status) {
-        psErrorStackPrint(stderr, "Can't find input data!\n");
-        ppImageCleanup(config, options);
-        exit(PS_EXIT_PROG_ERROR);
-    }
-
-    pmFPAview *view = pmFPAviewAlloc(0);// View for level of interest
-    pmHDU *lastHDU = NULL;              // Last HDU that was updated
-
-    // files associated with the science image
-    if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) ESCAPE ("load failure for FPA");
-
-    while ((chip = pmFPAviewNextChip(view, input->fpa, 1)) != NULL) {
-        psLogMsg ("ppImageLoop", 4, "Chip %d: %x %x\n", view->chip, chip->file_exists, chip->process);
-        if (!chip->process || !chip->file_exists) {
-            continue;
-        }
-        if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) ESCAPE ("load failure for Chip");
-
-        while ((cell = pmFPAviewNextCell(view, input->fpa, 1)) != NULL) {
-            psLogMsg ("ppImageLoop", 5, "Cell %d: %x %x\n", view->cell, cell->file_exists, cell->process);
-            if (!cell->process || !cell->file_exists) {
-                continue;
-            }
-            if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) ESCAPE ("load failure for Cell");
-
-            // Put version information into the header
-            pmHDU *hdu = pmHDUFromCell(cell);
-            if (hdu && hdu != lastHDU) {
-                ppImageVersionMetadata(hdu->header);
-                lastHDU = hdu;
-            }
-
-            // process each of the readouts
-            while ((readout = pmFPAviewNextReadout (view, input->fpa, 1)) != NULL) {
-                if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) ESCAPE ("load failure for Readout");
-                if (!readout->data_exists) {
-                    continue;
-                }
-
-		// XXX set the options->*Mask values here (after the mask images have been loaded
-		// and before any of the value are used)
-		if (!ppImageSetMaskBits (config, options)) ESCAPE ("Unable to set bit masks");
-		
-		if (!ppImageDetrendReadout(config, options, view)) ESCAPE ("Unable to detrend readout");
-            }
-        }
-
-        // Apply the fringe correction
-        if (options->doFringe) {
-	    if (!ppImageDetrendFringeApply (config, chip, view, options)) 
-		ESCAPE ("Unable to defringe");
-	}
-	
-	// measure various statistics for this image
-	if (!ppImagePixelStats (config, options, view)) 
-	    ESCAPE ("Unable to measures stats for image");
-
-        if (!ppImageMosaicChip(config, options, view, "PPIMAGE.CHIP", "PPIMAGE.OUTPUT"))
-            ESCAPE ("Unable to mosaic chip");
-
-        // we perform photometry on the readouts of this chip in the output
-        if (options->doPhotom) {
-            if (!ppImagePhotom(config, view)) ESCAPE ("error running photometry.");
-        }
-
-	// replace the masked pixels with the background level
-	if (options->replaceMasked) {
-	    if (!ppImageReplaceBackground (config, view, options)) 
-		ESCAPE ("Unable to replace masked pixels with background level");
-	}
-
-	// binning (used for display) must take place after the background is replaced, if desired
-        if (!ppImageRebinChip(config, view, options, "PPIMAGE.BIN1"))
-            ESCAPE ("Unable to bin chip (level 1).");
-
-        if (!ppImageRebinChip(config, view, options, "PPIMAGE.BIN2"))
-            ESCAPE ("Unable to bin chip (level 2).");
-
-        // Close cells (XXX shouldn't pmFPAfileClose iterate down as needed?)
-        view->cell = -1;
-        while ((cell = pmFPAviewNextCell(view, input->fpa, 1)) != NULL) {
-            if (!cell->process || !cell->file_exists) {
-                continue;
-            }
-            if (!pmFPAfileIOChecks(config, view, PM_FPA_AFTER)) ESCAPE ("save failure for Cell");
-        }
-
-        // Close chip
-        if (!pmFPAfileIOChecks(config, view, PM_FPA_AFTER)) ESCAPE ("save failure for Chip");
-    }
-
-    // generate the full-scale FPA mosaic
-    if (!ppImageMosaicFPA(config, options, "PPIMAGE.OUTPUT.FPA1", "PPIMAGE.BIN1")) ESCAPE ("failure in FPA Mosaic (level 1)");
-    if (!ppImageMosaicFPA(config, options, "PPIMAGE.OUTPUT.FPA2", "PPIMAGE.BIN2")) ESCAPE ("failure in FPA Mosaic (level 2)");
-
-    // we perform astrometry on all chips after sources have been detected
-    // this also performs the psastro file IO
-    if (options->doAstromChip || options->doAstromMosaic) {
-        if (!ppImageAstrom(config)) ESCAPE ("error running astrometry.");
-    }
-
-    if (psTraceGetLevel("ppImage") >= 3) {
-	ppImageFileCheck (config);
-    }
-
-    // Write out summary statistics
-    if (!ppImageMetadataStats (config, options)) ESCAPE ("Unable to write statistics file.");
-
-    // Write out summary statistics
-    if (!ppImageStatsOutput (config, options)) ESCAPE ("Unable to write statistics file.");
-
-    // Output and Close FPA
-    if (!pmFPAfileIOChecks(config, view, PM_FPA_AFTER)) ESCAPE ("save failure for FPA");
-
-    psFree(view);
-    return true;
-}
Index: /branches/cnb_branches/cnb_branch_20090301/ppImage/src/ppImageOptions.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppImage/src/ppImageOptions.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ppImage/src/ppImageOptions.c	(revision 23352)
@@ -81,4 +81,7 @@
     options->remnanceThresh  = 25.0;    // Threshold for remnance detection
 
+    // per-class normalization source
+    options->normClass       = NULL;    // per-class normalizations refer to this class
+
     return options;
 }
@@ -278,4 +281,7 @@
     options->remnanceThresh = psMetadataLookupS32(NULL, recipe, "REMNANCE.THRESH");
 
+    // per-class normalization source (just a reference; don't free)
+    options->normClass = psMetadataLookupStr(NULL, recipe, "NORM.CLASS");
+
     return options;
 }
Index: /branches/cnb_branches/cnb_branch_20090301/ppImage/src/ppImageParseCamera.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppImage/src/ppImageParseCamera.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ppImage/src/ppImageParseCamera.c	(revision 23352)
@@ -9,44 +9,16 @@
     bool status = false;
 
-    // the input image defines the camera, and all recipes and options the follow
-    pmFPAfile *input = pmFPAfileDefineFromArgs (&status, config, "PPIMAGE.INPUT", "INPUT");
-    if (!status || !input) {
-        psError(PS_ERR_IO, false, "Failed to build FPA from PPIMAGE.INPUT");
-        return NULL;
-    }
-    if (input->type != PM_FPA_FILE_IMAGE) {
-        psError(PS_ERR_IO, true, "PPIMAGE.INPUT is not of type IMAGE");
-        return NULL;
-    }
-
-    // if MASK or VARIANCE was supplied on command line, bind files to 'input'.
-    // the mask and variance will be mosaicked with the image
-    pmFPAfile *inputMask = pmFPAfileBindFromArgs(&status, input, config, "PPIMAGE.INPUT.MASK", "PPIMAGE.INPUT.MASK");
-    if (!status) {
-        psError (PS_ERR_UNKNOWN, false, "failed to load find definition");
-        return NULL;
-    }
-    if (inputMask) {
-      if (inputMask->type != PM_FPA_FILE_MASK) {
-        psError(PS_ERR_IO, true, "PPIMAGE.INPUT.MASK is not of type MASK");
-        return NULL;
-      }
-    }
-
-    pmFPAfile *inputVariance = pmFPAfileBindFromArgs(&status, input, config, "PPIMAGE.INPUT.VARIANCE", "PPIMAGE.INPUT.VARIANCE");
-    if (!status) {
-        psError (PS_ERR_UNKNOWN, false, "failed to load find definition");
-        return NULL;
-    }
-    if (inputVariance && inputVariance->type != PM_FPA_FILE_VARIANCE) {
-        psError(PS_ERR_IO, true, "PPIMAGE.INPUT.VARIANCE is not of type VARIANCE");
-        return NULL;
-    }
+    if (!ppImageDefineFile(config, NULL, "PPIMAGE.INPUT", "INPUT", PM_FPA_FILE_IMAGE, PM_DETREND_TYPE_NONE)) {
+        psError(PS_ERR_IO, false, "Can't find an input image source");
+        return NULL;
+    }
+    pmFPAfile *input = psMetadataLookupPtr(NULL, config->files, "PPIMAGE.INPUT"); // Input file
+    psAssert(input, "We just put it there!");
 
     // add recipe options supplied on command line
-    psMetadata *recipe  = psMetadataLookupPtr (&status, config->recipes, RECIPE_NAME);
+    psMetadata *recipe  = psMetadataLookupPtr(&status, config->recipes, RECIPE_NAME);
 
     // parse the options from the metadata format to the ppImageOptions structure
-    ppImageOptions *options = ppImageOptionsParse (config);
+    ppImageOptions *options = ppImageOptionsParse(config);
 
     // the following are defined from the argument list, if given,
@@ -54,27 +26,33 @@
     // not all input or output images are used in a given recipe
     if (options->doBias) {
-        if (!ppImageDefineFile (config, input->fpa, "PPIMAGE.BIAS", "BIAS", PM_FPA_FILE_IMAGE, PM_DETREND_TYPE_BIAS)) {
-            psError (PS_ERR_IO, false, "Can't find a bias image source");
-            psFree (options);
+        if (!ppImageDefineFile(config, input->fpa, "PPIMAGE.BIAS", "BIAS",
+                               PM_FPA_FILE_IMAGE, PM_DETREND_TYPE_BIAS)) {
+            psError(PS_ERR_IO, false, "Can't find a bias image source");
+            psFree(options);
             return NULL;
         }
     }
     if (options->doDark) {
-        if (!ppImageDefineFile (config, input->fpa, "PPIMAGE.DARK", "DARK", PM_FPA_FILE_DARK, PM_DETREND_TYPE_DARK)) {
-            psError (PS_ERR_IO, false, "Can't find a dark image source");
-            psFree (options);
+        if (!ppImageDefineFile(config, input->fpa, "PPIMAGE.DARK", "DARK",
+                               PM_FPA_FILE_DARK, PM_DETREND_TYPE_DARK)) {
+            psError(PS_ERR_IO, false, "Can't find a dark image source");
+            psFree(options);
             return NULL;
         }
     }
     if (options->doMask) {
-
-        if (!ppImageDefineFile (config, input->fpa, "PPIMAGE.MASK", "MASK", PM_FPA_FILE_MASK, PM_DETREND_TYPE_MASK)) {
-            psError (PS_ERR_IO, false, "Can't find a mask image source");
-            psFree (options);
-            return NULL;
-        }
+        if (!ppImageDefineFile(config, input->fpa, "PPIMAGE.MASK", "MASK",
+                               PM_FPA_FILE_MASK, PM_DETREND_TYPE_MASK)) {
+            psError(PS_ERR_IO, false, "Can't find a mask image source");
+            psFree(options);
+            return NULL;
+        }
+
+#if 0
+        // I think this is now done automatically in the pmFPAfileDefine and pmFPAfileIOChecks -- PAP.
+
         // XXX have ppImageDefineFile return the pmFPAfile?
         pmFPAfile *mask = psMetadataLookupPtr(&status, config->files, "PPIMAGE.MASK");
-        psAssert (mask, "mask not defined?  not possible!");
+        psAssert(mask, "Just defined the mask!");
 
         // Need to read the names of bit masks from the mask header and set them in the
@@ -84,5 +62,5 @@
             // XXX need to load the mask bit names from one of the headers
             // this grabs the first available hdu : no guarantee that it will be valid, though
-            pmHDU *hdu = pmHDUGetFirst (mask->fpa);
+            pmHDU *hdu = pmHDUGetFirst(mask->fpa);
             if (!hdu) {
                 psError(PS_ERR_IO, true, "no valid HDU for PPIMAGE.INPUT.MASK");
@@ -90,14 +68,16 @@
             }
             // XXX is this consistent with the pmConfigMaskReadHeader call above?
-            if (!pmConfigMaskReadHeader (config, hdu->header)) {
+            if (!pmConfigMaskReadHeader(config, hdu->header)) {
                 psError(PS_ERR_IO, false, "error in mask bits");
                 return NULL;
             }
         }
+#endif
     }
     if (options->doShutter) {
-        if (!ppImageDefineFile (config, input->fpa, "PPIMAGE.SHUTTER", "SHUTTER", PM_FPA_FILE_IMAGE, PM_DETREND_TYPE_SHUTTER)) {
-            psError (PS_ERR_IO, false, "Can't find a shutter image source");
-            psFree (options);
+        if (!ppImageDefineFile(config, input->fpa, "PPIMAGE.SHUTTER", "SHUTTER",
+                               PM_FPA_FILE_IMAGE, PM_DETREND_TYPE_SHUTTER)) {
+            psError(PS_ERR_IO, false, "Can't find a shutter image source");
+            psFree(options);
             return NULL;
         }
@@ -105,15 +85,16 @@
 
     if (options->doFlat) {
-        if (!ppImageDefineFile (config, input->fpa, "PPIMAGE.FLAT", "FLAT", PM_FPA_FILE_IMAGE, PM_DETREND_TYPE_FLAT)) {
-            psError (PS_ERR_IO, false, "Can't find a flat image source");
-            psFree (options);
-            return NULL;
-        }
-    }
-
-    int nThreads = psMetadataLookupS32 (&status, config->arguments, "NTHREADS");
+        if (!ppImageDefineFile(config, input->fpa, "PPIMAGE.FLAT", "FLAT",
+                               PM_FPA_FILE_IMAGE, PM_DETREND_TYPE_FLAT)) {
+            psError(PS_ERR_IO, false, "Can't find a flat image source");
+            psFree(options);
+            return NULL;
+        }
+    }
+
+    int nThreads = psMetadataLookupS32(&status, config->arguments, "NTHREADS");
     if (nThreads > 0) {
-        int nScanRows = psMetadataLookupS32 (&status, recipe, "SCAN.ROWS");
-        pmDetrendSetThreadTasks (nScanRows);
+        int nScanRows = psMetadataLookupS32(&status, recipe, "SCAN.ROWS");
+        pmDetrendSetThreadTasks(nScanRows);
     }
 
@@ -166,5 +147,6 @@
 skip_fringe:
     if (options->doFringe) {
-        if (!ppImageDefineFile (config, input->fpa, "PPIMAGE.FRINGE", "FRINGE", PM_FPA_FILE_FRINGE, PM_DETREND_TYPE_FRINGE)) {
+        if (!ppImageDefineFile(config, input->fpa, "PPIMAGE.FRINGE", "FRINGE",
+                               PM_FPA_FILE_FRINGE, PM_DETREND_TYPE_FRINGE)) {
             psError (PS_ERR_IO, false, "Can't find a fringe image source");
             return NULL;
@@ -283,5 +265,6 @@
         // define associated psphot input/output files
         if (!psphotDefineFiles (config, psphotInput)) {
-            psError(PSPHOT_ERR_CONFIG, false, "Trouble defining the additional input/output files for psphot");
+            psError(PSPHOT_ERR_CONFIG, false,
+                    "Trouble defining the additional input/output files for psphot");
             return false;
         }
@@ -291,23 +274,25 @@
     if (options->doAstromChip || options->doAstromMosaic) {
         if (!options->doPhotom) {
-            psError (PSASTRO_ERR_CONFIG, false, "photometry mode is not selected; it is required for astrometry");
+            psError(PSASTRO_ERR_CONFIG, false,
+                    "Photometry mode is not selected; it is required for astrometry");
             return false;
         }
 
-        pmFPAfile *psphotOutput = psMetadataLookupPtr (&status, config->files, "PSPHOT.OUTPUT");
-        PS_ASSERT (psphotOutput, false);
-
-        pmFPAfile *psastroInput = pmFPAfileDefineInput (config, psphotOutput->fpa, NULL, "PSASTRO.INPUT");
-        PS_ASSERT (psastroInput, false);
+        pmFPAfile *psphotOutput = psMetadataLookupPtr(&status, config->files, "PSPHOT.OUTPUT");
+        PS_ASSERT(psphotOutput, false);
+
+        pmFPAfile *psastroInput = pmFPAfileDefineInput(config, psphotOutput->fpa, NULL, "PSASTRO.INPUT");
+        PS_ASSERT(psastroInput, false);
         psastroInput->mode = PM_FPA_MODE_REFERENCE;
 
         // define associated psphot input/output files
-        if (!psastroDefineFiles (config, psastroInput)) {
-            psError(PSPHOT_ERR_CONFIG, false, "Trouble defining the additional input/output files for psastro");
+        if (!psastroDefineFiles(config, psastroInput)) {
+            psError(PSPHOT_ERR_CONFIG, false,
+                    "Trouble defining the additional input/output files for psastro");
             return false;
         }
 
         // deactivate the psastro files, reactive when needed
-        pmFPAfileActivate (config->files, false, "PSASTRO.OUTPUT");
+        pmFPAfileActivate(config->files, false, "PSASTRO.OUTPUT");
     }
 
@@ -325,7 +310,7 @@
 
     // outImage is used as a carrier: input to chipImage -> require the data to remain at the CHIP level
-    outImage->freeLevel = PS_MIN (outImage->freeLevel, PM_FPA_LEVEL_CHIP);
+    outImage->freeLevel = PS_MIN(outImage->freeLevel, PM_FPA_LEVEL_CHIP);
     outImage->dataLevel = outImage->freeLevel;
-    outImage->fileLevel = PS_MIN (outImage->fileLevel, outImage->dataLevel);
+    outImage->fileLevel = PS_MIN(outImage->fileLevel, outImage->dataLevel);
 
     // outMask and outVariance must be freed at the same level as outImage (all freed by pmFPAFreeData)
@@ -342,8 +327,9 @@
 
     // the input data is the same as the outImage data : force the free levels to match
-    input->freeLevel = PS_MIN (outImage->freeLevel, input->freeLevel);
+    input->freeLevel = PS_MIN(outImage->freeLevel, input->freeLevel);
 
     // define the binned target files (which may just be carriers for some camera configurations)
-    pmFPAfile *bin1 = pmFPAfileDefineFromFPA (config, chipImage->fpa, options->xBin1, options->yBin1, "PPIMAGE.BIN1");
+    pmFPAfile *bin1 = pmFPAfileDefineFromFPA(config, chipImage->fpa, options->xBin1, options->yBin1,
+                                             "PPIMAGE.BIN1");
     if (!bin1) {
         psError(PS_ERR_IO, false, _("Unable to generate new file from PPIMAGE.BIN1"));
@@ -357,5 +343,6 @@
     }
 
-    pmFPAfile *bin2 = pmFPAfileDefineFromFPA (config, chipImage->fpa, options->xBin2, options->yBin2, "PPIMAGE.BIN2");
+    pmFPAfile *bin2 = pmFPAfileDefineFromFPA(config, chipImage->fpa, options->xBin2, options->yBin2,
+                                             "PPIMAGE.BIN2");
     if (!bin2) {
         psError(PS_ERR_IO, false, _("Unable to generate new file from PPIMAGE.BIN2"));
@@ -373,5 +360,5 @@
     bin2->freeLevel = PM_FPA_LEVEL_FPA;
 
-    pmFPAfile *jpg1 = pmFPAfileDefineOutput (config, byFPA1->fpa, "PPIMAGE.JPEG1");
+    pmFPAfile *jpg1 = pmFPAfileDefineOutput(config, byFPA1->fpa, "PPIMAGE.JPEG1");
     if (!jpg1) {
         psError(PS_ERR_IO, false, _("Unable to generate new file from PPIMAGE.JPEG1"));
@@ -384,5 +371,5 @@
         return NULL;
     }
-    pmFPAfile *jpg2 = pmFPAfileDefineOutput (config, byFPA2->fpa, "PPIMAGE.JPEG2");
+    pmFPAfile *jpg2 = pmFPAfileDefineOutput(config, byFPA2->fpa, "PPIMAGE.JPEG2");
     if (!jpg2) {
         psError(PS_ERR_IO, false, _("Unable to generate new file from PPIMAGE.JPEG2"));
@@ -404,5 +391,5 @@
     // Chip selection: turn on only the chips specified (pass status to suppress missing-key log msg)
     char *chipLine = psMetadataLookupStr(&status, config->arguments, "CHIP_SELECTIONS");
-    psArray *chips = psStringSplitArray (chipLine, ",", false);
+    psArray *chips = psStringSplitArray(chipLine, ",", false);
     if (chips->n > 0) {
         pmFPASelectChip (input->fpa, -1, true); // deselect all chips
@@ -454,5 +441,5 @@
     if (psTraceGetLevel("ppImage.config") > 0) {
         // Get a look inside all the files.
-        psMetadataIterator *filesIter = psMetadataIteratorAlloc(config->files, PS_LIST_HEAD, NULL); // Iterator
+        psMetadataIterator *filesIter = psMetadataIteratorAlloc(config->files, PS_LIST_HEAD, NULL);
         psMetadataItem *item;               // Item from iteration
         fprintf(stderr, "Files:\n");
Index: /branches/cnb_branches/cnb_branch_20090301/ppImage/src/ppImageReplaceBackground.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppImage/src/ppImageReplaceBackground.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ppImage/src/ppImageReplaceBackground.c	(revision 23352)
@@ -101,5 +101,6 @@
         }
     }
-    psImageBinning *binning = psMetadataLookupPtr(&status, psphotRecipe, "PSPHOT.BACKGROUND.BINNING"); // Binning for model
+    psImageBinning *binning = psMetadataLookupPtr(&status, modelRO->analysis,
+                                                  "PSPHOT.BACKGROUND.BINNING"); // Binning for model
     if (!binning) {
         psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find background binning");
Index: /branches/cnb_branches/cnb_branch_20090301/ppImage/src/ppImageVersion.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppImage/src/ppImageVersion.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ppImage/src/ppImageVersion.c	(revision 23352)
@@ -5,26 +5,89 @@
 #include "ppImage.h"
 
-static const char *cvsTag = "$Name: not supported by cvs2svn $";// CVS tag name
+#ifndef PPIMAGE_VERSION
+#error "PPIMAGE_VERSION is not set"
+#endif
+#ifndef PPIMAGE_BRANCH
+#error "PPIMAGE_BRANCH is not set"
+#endif
+#ifndef PPIMAGE_SOURCE
+#error "PPIMAGE_SOURCE is not set"
+#endif
+
+#define xstr(s) str(s)
+#define str(s) #s
 
 psString ppImageVersion(void)
 {
-    psString version = NULL;            // Version, to return
-    psStringAppend(&version, "%s-%s",PACKAGE_NAME,PACKAGE_VERSION);
-    return version;
+    char *value = NULL;
+    psStringAppend(&value, "%s@%s", xstr(PPIMAGE_BRANCH), xstr(PPIMAGE_VERSION));
+    return value;
+}
+
+psString ppImageSource(void)
+{
+    return psStringCopy (xstr(PPIMAGE_SOURCE));
 }
 
 psString ppImageVersionLong(void)
 {
-    psString version = ppImageVersion(); // Version, to return
-    psString tag = psStringStripCVS(cvsTag, "Name"); // CVS tag
-    psStringAppend(&version, " (cvs tag %s) %s, %s", tag, __DATE__, __TIME__);
-    psFree(tag);
+    psString version = ppImageVersion();  // Version, to return
+    psString source = ppImageSource(); // Source
+
+    psStringPrepend(&version, "ppImage ");
+    psStringAppend(&version, " from %s, built %s, %s", source, __DATE__, __TIME__);
+    psFree(source);
+
+#ifdef __OPTIMIZE__
+    psStringAppend(&version, " optimised");
+#else
+    psStringAppend(&version, " unoptimised");
+#endif
+
     return version;
+};
+
+bool ppImageVersionHeader(psMetadata *header)
+{
+    PS_ASSERT_METADATA_NON_NULL(header, false);
+
+    psTime *time = psTimeGetNow(PS_TIME_TAI); // The time now
+    psString timeString = psTimeToISO(time); // The time in an ISO string
+    psFree(time);
+    psString history = NULL;               // History string
+    psStringAppend(&history, "ppImage at %s", timeString);
+    psFree(timeString);
+    psMetadataAddStr(header, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, NULL, history);
+    psFree(history);
+
+    psLibVersionHeader(header);
+    psModulesVersionHeader(header);
+    psphotVersionHeader(header);
+    psastroVersionHeader(header);
+    ppStatsVersionHeader(header);
+
+    psString version = ppImageVersion(); // ppImage software version
+    psString source  = ppImageSource();  // ppImage software source
+
+    psStringPrepend(&version, "ppImage version: ");
+    psStringPrepend(&source, "ppImage source: ");
+
+    psMetadataAddStr(header, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, NULL, version);
+    psMetadataAddStr(header, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, NULL, source);
+
+    psFree(version);
+    psFree(source);
+
+    return true;
 }
 
 
-void ppImageVersionMetadata(psMetadata *metadata)
+void ppImageVersionPrint(void)
 {
-    PS_ASSERT_METADATA_NON_NULL(metadata,);
+    psTime *time = psTimeGetNow(PS_TIME_TAI); // The time now
+    psString timeString = psTimeToISO(time); // The time in an ISO string
+    psFree(time);
+    psLogMsg("ppImage", PS_LOG_INFO, "ppImage at %s", timeString);
+    psFree(timeString);
 
     psString pslib = psLibVersionLong();// psLib version
@@ -35,20 +98,11 @@
     psString ppImage = ppImageVersionLong(); // ppImage version
 
-    psTime *time = psTimeGetNow(PS_TIME_TAI); // The time now
-    psString timeString = psTimeToISO(time); // The time in an ISO string
-    psFree(time);
-    psString head = NULL;               // Head string
-    psStringAppend(&head, "ppImage processing at %s. Component information:", timeString);
-    psFree(timeString);
+    psLogMsg("ppImage", PS_LOG_INFO, "%s", pslib);
+    psLogMsg("ppImage", PS_LOG_INFO, "%s", psmodules);
+    psLogMsg("ppImage", PS_LOG_INFO, "%s", psphot);
+    psLogMsg("ppImage", PS_LOG_INFO, "%s", psastro);
+    psLogMsg("ppImage", PS_LOG_INFO, "%s", ppStats);
+    psLogMsg("ppImage", PS_LOG_INFO, "%s", ppImage);
 
-    psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, head, "");
-    psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, pslib, "");
-    psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, psmodules, "");
-    psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, psphot, "");
-    psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, psastro, "");
-    psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, ppStats, "");
-    psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, ppImage, "");
-
-    psFree(head);
     psFree(pslib);
     psFree(psmodules);
Index: /branches/cnb_branches/cnb_branch_20090301/ppMerge/src/Makefile.am
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppMerge/src/Makefile.am	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ppMerge/src/Makefile.am	(revision 23352)
@@ -1,5 +1,14 @@
 bin_PROGRAMS = ppMerge
 
-ppMerge_CFLAGS = $(PPMERGE_CFLAGS) $(PPSTATS_CFLAGS) $(PSMODULE_CFLAGS) $(PSLIB_CFLAGS)
+# PPMERGE_VERSION=`if [ -e ../../VERSION ]; then cat ../../VERSION; else svnversion; fi`
+# PPMERGE_BRANCH=`if [ -e ../../BRANCH ]; then cat ../../BRANCH; else svn info | sed -n -e '/URL:/ h' -e '/Repository Root:/ { x; H; x; s|Repository Root: \(.*\)\nURL: \1\(.*\)|\2| ; s|^/|| ; s|/[a-zA-Z]*/src.*|| ; p }'; fi`
+# PPMERGE_SOURCE=`if [ -e ../../SOURCE ]; then cat ../../SOURCE; else svn info | sed -n -e 's/Repository UUID: // p'; fi`
+# 
+# # Force recompilation of ppMergeVersion.c, since it gets the version information
+# ppMergeVersion.c: FORCE
+# 	touch ppMergeVersion.c
+# FORCE: ;
+
+ppMerge_CFLAGS = $(PPMERGE_CFLAGS) $(PPSTATS_CFLAGS) $(PSMODULE_CFLAGS) $(PSLIB_CFLAGS) -DPPMERGE_VERSION=$(SVN_VERSION) -DPPMERGE_BRANCH=$(SVN_BRANCH) -DPPMERGE_SOURCE=$(SVN_SOURCE)
 ppMerge_LDFLAGS = $(PPMERGE_LIBS) $(PPSTATS_LIBS) $(PSMODULE_LIBS) $(PSLIB_LIBS)
 
@@ -14,5 +23,6 @@
 	ppMergeLoop_Threaded.c  \
 	ppMergeSetThreads.c	\
-	ppMergeMask.c
+	ppMergeMask.c		\
+	ppMergeVersion.c
 
 #	ppMergeLoop.c		
Index: /branches/cnb_branches/cnb_branch_20090301/ppMerge/src/ppMerge.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppMerge/src/ppMerge.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ppMerge/src/ppMerge.c	(revision 23352)
@@ -38,4 +38,6 @@
         goto die;
     }
+
+    ppMergeVersionPrint();
 
     ppMergeType type = psMetadataLookupS32(NULL, config->arguments, "TYPE"); ///< Type of frame
Index: /branches/cnb_branches/cnb_branch_20090301/ppMerge/src/ppMerge.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppMerge/src/ppMerge.h	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ppMerge/src/ppMerge.h	(revision 23352)
@@ -28,7 +28,7 @@
 /// @{
 
-#define TIMERNAME "ppMerge"             ///< Name for timer 
-#define PPMERGE_RECIPE "PPMERGE"        ///< Recipe name 
-#define THREADED 1                      ///< Compile with threads? 
+#define TIMERNAME "ppMerge"             ///< Name for timer
+#define PPMERGE_RECIPE "PPMERGE"        ///< Recipe name
+#define THREADED 1                      ///< Compile with threads?
 
 /**
@@ -36,11 +36,11 @@
  */
 typedef enum {
-    PPMERGE_TYPE_UNKNOWN,               ///< Unknown type 
-    PPMERGE_TYPE_BIAS,                  ///< Bias frame 
-    PPMERGE_TYPE_DARK,                  ///< (Multi-)Dark frame 
-    PPMERGE_TYPE_MASK,                  ///< Mask frame 
-    PPMERGE_TYPE_SHUTTER,               ///< Shutter frame 
+    PPMERGE_TYPE_UNKNOWN,               ///< Unknown type
+    PPMERGE_TYPE_BIAS,                  ///< Bias frame
+    PPMERGE_TYPE_DARK,                  ///< (Multi-)Dark frame
+    PPMERGE_TYPE_MASK,                  ///< Mask frame
+    PPMERGE_TYPE_SHUTTER,               ///< Shutter frame
     PPMERGE_TYPE_FLAT,                  ///< Flat-field frame (dome or sky)
-    PPMERGE_TYPE_FRINGE,                ///< Fringe frame 
+    PPMERGE_TYPE_FRINGE,                ///< Fringe frame
 } ppMergeType;
 
@@ -49,7 +49,7 @@
  */
 typedef enum {
-    PPMERGE_FILES_ALL,                  ///< All files 
-    PPMERGE_FILES_INPUT,                ///< Input files 
-    PPMERGE_FILES_OUTPUT                ///< Output files 
+    PPMERGE_FILES_ALL,                  ///< All files
+    PPMERGE_FILES_INPUT,                ///< Input files
+    PPMERGE_FILES_OUTPUT                ///< Output files
 } ppMergeFiles;
 
@@ -60,9 +60,9 @@
  */
 typedef struct {
-    psArray *readouts;                  ///< Input readouts 
-    bool read;                          ///< Has the scan been read? 
-    bool busy;                          ///< Is the scan being processed? 
-    int firstScan;                      ///< First row of the chunk to be read for this group 
-    int lastScan;                       ///< Last row of the chunk to be read for this group 
+    psArray *readouts;                  ///< Input readouts
+    bool read;                          ///< Has the scan been read?
+    bool busy;                          ///< Is the scan being processed?
+    int firstScan;                      ///< First row of the chunk to be read for this group
+    int lastScan;                       ///< Last row of the chunk to be read for this group
 } ppMergeFileGroup;
 
@@ -71,5 +71,5 @@
  */
 bool ppMergeArguments(int argc, char *argv[], ///< Command-line arguments
-                      pmConfig *config  ///< Configuration 
+                      pmConfig *config  ///< Configuration
     );
 
@@ -77,5 +77,5 @@
  * Set up camera files
  */
-bool ppMergeCamera(pmConfig *config     ///< Configuration 
+bool ppMergeCamera(pmConfig *config     ///< Configuration
     );
 
@@ -83,5 +83,5 @@
  * Measure scale and zero-points
  */
-bool ppMergeScaleZero(pmConfig *config  ///< Configuration 
+bool ppMergeScaleZero(pmConfig *config  ///< Configuration
     );
 
@@ -89,5 +89,5 @@
  * Main loop to do the merging
  */
-bool ppMergeLoop(pmConfig *config       ///< Configuration 
+bool ppMergeLoop(pmConfig *config       ///< Configuration
     );
 
@@ -95,5 +95,5 @@
  * Main loop for masks
  */
-bool ppMergeMask(pmConfig *config       ///< Configuration 
+bool ppMergeMask(pmConfig *config       ///< Configuration
     );
 
@@ -101,8 +101,8 @@
  * Read nominated input file
  */
-bool ppMergeFileReadInput(pmConfig *config, ///< Configuration 
-                          pmReadout *readout, ///< Readout into which to read 
-                          int num,      ///< Number of file in sequence 
-                          int rows      ///< Number of rows to read at once 
+bool ppMergeFileReadInput(pmConfig *config, ///< Configuration
+                          pmReadout *readout, ///< Readout into which to read
+                          int num,      ///< Number of file in sequence
+                          int rows      ///< Number of rows to read at once
     );
 
@@ -110,7 +110,7 @@
  * Open nominated input file
  */
-bool ppMergeFileOpenInput(pmConfig *config, ///< Configuration 
-                          const pmFPAview *view, ///< View to open 
-                          int num       ///< Number of file in sequence 
+bool ppMergeFileOpenInput(pmConfig *config, ///< Configuration
+                          const pmFPAview *view, ///< View to open
+                          int num       ///< Number of file in sequence
     );
 
@@ -166,4 +166,23 @@
 bool ppMergeSetThreads(void);
 
+
+/// Return software version
+psString ppMergeVersion(void);
+
+/// Return software source
+psString ppMergeSource(void);
+
+/// Return detailed software version information
+psString ppMergeVersionLong(void);
+
+/// Populate a FITS header with version information
+bool ppMergeVersionHeader(
+    psMetadata *header                  ///< Header to populate
+    );
+
+/// Print version information
+void ppMergeVersionPrint(void);
+
+
 ///@}
 #endif
Index: /branches/cnb_branches/cnb_branch_20090301/ppMerge/src/ppMergeLoop_Threaded.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppMerge/src/ppMergeLoop_Threaded.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ppMerge/src/ppMergeLoop_Threaded.c	(revision 23352)
@@ -137,4 +137,5 @@
     assert(output && output->fpa);
     pmFPA *outFPA = output->fpa;        ///< Output FPA
+    pmHDU *lastHDU = NULL;              // Last HDU that was updated
     int cellNum = 0;                    ///< Index of cell
     if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
@@ -156,4 +157,13 @@
                 // No data here
                 continue;
+            }
+
+            // Update the header
+            {
+                pmHDU *hdu = pmHDUGetHighest(outFPA, outChip, outCell); // HDU for file
+                if (hdu && hdu != lastHDU) {
+                    ppMergeVersionHeader(hdu->header);
+                    lastHDU = hdu;
+                }
             }
 
Index: /branches/cnb_branches/cnb_branch_20090301/ppMerge/src/ppMergeMask.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppMerge/src/ppMergeMask.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ppMerge/src/ppMergeMask.c	(revision 23352)
@@ -16,4 +16,5 @@
                       const pmFPAview *view, ///< View to chip
                       bool writeOut,     ///< Write output?
+                      pmHDU **lastHDU,   ///< HDU last updated
                       psRandom *rng,    ///< Random number generator
                       psMetadata *stats ///< Statistics output
@@ -96,4 +97,13 @@
                     i, inView->chip, inView->cell);
 
+            // Update the header
+            {
+                pmHDU *hdu = pmHDUGetHighest(outCell->parent->parent, outCell->parent, outCell); // File HDU
+                if (hdu && hdu != *lastHDU) {
+                    ppMergeVersionHeader(hdu->header);
+                    *lastHDU = hdu;
+                }
+            }
+
             if (!pmFPAfileIOChecks(config, inView, PM_FPA_BEFORE)) {
                 psFree(inView);
@@ -359,5 +369,5 @@
 
     pmFPAview *view = pmFPAviewAlloc(0); ///< View to component of interest
-    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 0); ///< Random number generator
+    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS); ///< Random number generator
 
     psMetadata *stats = NULL;           ///< Statistics for output
@@ -395,4 +405,5 @@
     assert(output && output->fpa);
     pmFPA *outFPA = output->fpa;        ///< Output FPA
+    pmHDU *lastHDU = NULL;              // Last HDU updated
 
     if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
@@ -409,5 +420,5 @@
 
         for (int i = 0; i < iter; i++) {
-            if (!mergeMask(config, view, (i == iter - 1), rng, stats)) {
+            if (!mergeMask(config, view, (i == iter - 1), &lastHDU, rng, stats)) {
                 psError(PS_ERR_UNKNOWN, false, "Unable to merge chip %d", view->chip);
                 goto PPMERGE_MASK_ERROR;
Index: /branches/cnb_branches/cnb_branch_20090301/ppMerge/src/ppMergeScaleZero.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppMerge/src/ppMergeScaleZero.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ppMerge/src/ppMergeScaleZero.c	(revision 23352)
@@ -51,5 +51,5 @@
         break;
     }
-    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 0); ///< Random number generator
+    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS); ///< Random number generator
     pmFPAview *view = NULL;             ///< View into FPA
 
Index: /branches/cnb_branches/cnb_branch_20090301/ppMerge/src/ppMergeVersion.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppMerge/src/ppMergeVersion.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ppMerge/src/ppMergeVersion.c	(revision 23352)
@@ -22,46 +22,97 @@
 #include "ppMergeVersion.h"
 
-static const char *cvsTag = "$Name: not supported by cvs2svn $";///< CVS tag name
+#ifndef PPMERGE_VERSION
+#error "PPMERGE_VERSION is not set"
+#endif
+#ifndef PPMERGE_BRANCH
+#error "PPMERGE_BRANCH is not set"
+#endif
+#ifndef PPMERGE_SOURCE
+#error "PPMERGE_SOURCE is not set"
+#endif
+
+#define xstr(s) str(s)
+#define str(s) #s
 
 psString ppMergeVersion(void)
 {
-    psString version = NULL;            ///< Version, to return
-    psStringAppend(&version, "%s-%s",PACKAGE_NAME,PACKAGE_VERSION);
-    return version;
+    char *value = NULL;
+    psStringAppend(&value, "%s@%s", xstr(PPMERGE_BRANCH), xstr(PPMERGE_VERSION));
+    return value;
+}
+
+psString ppMergeSource(void)
+{
+    return psStringCopy(xstr(PPMERGE_SOURCE));
 }
 
 psString ppMergeVersionLong(void)
 {
-    psString version = ppMergeVersion(); ///< Version, to return
-    psString tag = psStringStripCVS(cvsTag, "Name"); ///< CVS tag
-    psStringAppend(&version, " (cvs tag %s) %s, %s", tag, __DATE__, __TIME__);
-    psFree(tag);
+    psString version = ppMergeVersion();  // Version, to return
+    psString source = ppMergeSource();    // Source
+
+    psStringPrepend(&version, "ppMerge ");
+    psStringAppend(&version, " from %s, built %s, %s", source, __DATE__, __TIME__);
+    psFree(source);
+
+#ifdef __OPTIMIZE__
+    psStringAppend(&version, " optimised");
+#else
+    psStringAppend(&version, " unoptimised");
+#endif
+
     return version;
+};
+
+bool ppMergeVersionHeader(psMetadata *header)
+{
+    PS_ASSERT_METADATA_NON_NULL(header, false);
+
+    psTime *time = psTimeGetNow(PS_TIME_TAI); // The time now
+    psString timeString = psTimeToISO(time); // The time in an ISO string
+    psFree(time);
+    psString history = NULL;               // History string
+    psStringAppend(&history, "ppMerge at %s", timeString);
+    psFree(timeString);
+    psMetadataAddStr(header, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, NULL, history);
+    psFree(history);
+
+    psLibVersionHeader(header);
+    psModulesVersionHeader(header);
+    ppStatsVersionHeader(header);
+
+    psString version = ppMergeVersion(); // Software version
+    psString source  = ppMergeSource();  // Software source
+
+    psStringPrepend(&version, "ppMerge version: ");
+    psStringPrepend(&source, "ppMerge source: ");
+
+    psMetadataAddStr(header, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, NULL, version);
+    psMetadataAddStr(header, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, NULL, source);
+
+    psFree(version);
+    psFree(source);
+
+    return true;
 }
 
-
-void ppMergeVersionMetadata(psMetadata *metadata)
+void ppMergeVersionPrint(void)
 {
-    PS_ASSERT_METADATA_NON_NULL(metadata,);
-
-    psString pslib = psLibVersionLong();///< psLib version
-    psString psmodules = psModulesVersionLong(); ///< psModules version
-    psString ppStats = ppStatsVersionLong(); ///< ppStats version
-    psString ppMerge = ppMergeVersionLong(); ///< ppMerge version
-
-    psTime *time = psTimeGetNow(PS_TIME_TAI); ///< The time now
-    psString timeString = psTimeToISO(time); ///< The time in an ISO string
+    psTime *time = psTimeGetNow(PS_TIME_TAI); // The time now
+    psString timeString = psTimeToISO(time); // The time in an ISO string
     psFree(time);
-    psString head = NULL;               ///< Head string
-    psStringAppend(&head, "ppMerge processing at %s. Component information:", timeString);
+    psLogMsg("ppMerge", PS_LOG_INFO, "ppMerge at %s", timeString);
     psFree(timeString);
 
-    psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, head, "");
-    psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, pslib, "");
-    psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, psmodules, "");
-    psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, ppStats, "");
-    psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, ppMerge, "");
+    psString pslib = psLibVersionLong();// psLib version
+    psString psmodules = psModulesVersionLong(); // psModules version
+    psString ppStats = ppStatsVersionLong(); // ppStats version
+    psString ppMerge = ppMergeVersionLong(); // ppMerge version
 
-    psFree(head);
+    psLogMsg("ppImage", PS_LOG_INFO, "%s", pslib);
+    psLogMsg("ppImage", PS_LOG_INFO, "%s", psmodules);
+    psLogMsg("ppImage", PS_LOG_INFO, "%s", ppStats);
+    psLogMsg("ppImage", PS_LOG_INFO, "%s", ppMerge);
+
     psFree(pslib);
     psFree(psmodules);
Index: /branches/cnb_branches/cnb_branch_20090301/ppMerge/src/ppMergeVersion.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppMerge/src/ppMergeVersion.h	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ppMerge/src/ppMergeVersion.h	(revision 23352)
@@ -20,4 +20,9 @@
 
 /**
+ * Return software source
+ */
+psString ppMergeSource(void);
+
+/**
  * Return long version information
  */
Index: /branches/cnb_branches/cnb_branch_20090301/ppSim/src/Makefile.am
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppSim/src/Makefile.am	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ppSim/src/Makefile.am	(revision 23352)
@@ -1,5 +1,14 @@
 bin_PROGRAMS = ppSim ppSimSequence
 
-ppSim_CPPFLAGS = $(PSLIB_CFLAGS) $(PSMODULE_CFLAGS) $(PSPHOT_CFLAGS) $(PSASTRO_CFLAGS) $(ppSim_CFLAGS)
+# PPSIM_VERSION=`if [ -e ../../VERSION ]; then cat ../../VERSION; else svnversion; fi`
+# PPSIM_BRANCH=`if [ -e ../../BRANCH ]; then cat ../../BRANCH; else svn info | sed -n -e '/URL:/ h' -e '/Repository Root:/ { x; H; x; s|Repository Root: \(.*\)\nURL: \1\(.*\)|\2| ; s|^/|| ; s|/[a-zA-Z]*/src.*|| ; p }'; fi`
+# PPSIM_SOURCE=`if [ -e ../../SOURCE ]; then cat ../../SOURCE; else svn info | sed -n -e 's/Repository UUID: // p'; fi`
+# 
+# # Force recompilation of ppSimVersion.c, since it gets the version information
+# ppSimVersion.c: FORCE
+# 	touch ppSimVersion.c
+# FORCE: ;
+
+ppSim_CPPFLAGS = $(PSLIB_CFLAGS) $(PSMODULE_CFLAGS) $(PSPHOT_CFLAGS) $(PSASTRO_CFLAGS) $(ppSim_CFLAGS) -DPPSIM_VERSION=$(SVN_VERSION) -DPPSIM_BRANCH=$(SVN_BRANCH) -DPPSIM_SOURCE=$(SVN_SOURCE)
 ppSim_LDFLAGS = $(PSLIB_LIBS) $(PSMODULE_LIBS) $(PSPHOT_LIBS) $(PSASTRO_LIBS)
 ppSim_SOURCES = \
@@ -35,5 +44,6 @@
 	ppSimMosaicChip.c	  \
 	ppSimRandomGaussian.c	  \
-	ppSimBadPixels.c
+	ppSimBadPixels.c          \
+	ppSimVersion.c
 
 ppSimSequence_CPPFLAGS = $(PSLIB_CFLAGS) $(PSMODULE_CFLAGS) $(PSASTRO_CFLAGS) $(ppSim_CFLAGS)
Index: /branches/cnb_branches/cnb_branch_20090301/ppSim/src/ppSim.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppSim/src/ppSim.h	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ppSim/src/ppSim.h	(revision 23352)
@@ -19,7 +19,7 @@
 
 // Compare a value with minimum and maximum values, replacing where required.
-#define COMPARE(VALUE,MIN,MAX) {		\
-        if (VALUE < MIN) { MIN = VALUE; }	\
-        if (VALUE > MAX) { MAX = VALUE; }	\
+#define COMPARE(VALUE,MIN,MAX) {                \
+        if (VALUE < MIN) { MIN = VALUE; }       \
+        if (VALUE > MAX) { MAX = VALUE; }       \
     }
 
@@ -147,30 +147,30 @@
 float ppSimMagToFlux (float mag, float zp);
 
-float ppSimArgToRecipeF32(bool *status, 
-			  psMetadata *options,    // Target to which to add value
-			  const char *recipeName, // Name for value in the recipe
-			  psMetadata *arguments,  // Command-line arguments
-			  const char *argName	 // Argument name in the command-line arguments
+float ppSimArgToRecipeF32(bool *status,
+                          psMetadata *options,    // Target to which to add value
+                          const char *recipeName, // Name for value in the recipe
+                          psMetadata *arguments,  // Command-line arguments
+                          const char *argName    // Argument name in the command-line arguments
     );
 
 int ppSimArgToRecipeS32(bool *status,
-			psMetadata *options,    // Target to which to add value
-			const char *recipeName, // Name for value in the recipe
-			psMetadata *arguments,  // Command-line arguments
-			const char *argName	 // Argument name in the command-line arguments
+                        psMetadata *options,    // Target to which to add value
+                        const char *recipeName, // Name for value in the recipe
+                        psMetadata *arguments,  // Command-line arguments
+                        const char *argName      // Argument name in the command-line arguments
     );
 
 char *ppSimArgToRecipeStr(bool *status,
-			  psMetadata *options,    // Target to which to add value
-			  const char *recipeName, // Name for value in the recipe
-			  psMetadata *arguments,  // Command-line arguments
-			  const char *argName	 // Argument name in the command-line arguments
+                          psMetadata *options,    // Target to which to add value
+                          const char *recipeName, // Name for value in the recipe
+                          psMetadata *arguments,  // Command-line arguments
+                          const char *argName    // Argument name in the command-line arguments
     );
 
 bool ppSimArgToRecipeBool(bool *status,
-			  psMetadata *options,    // Target to which to add value
-			  const char *recipeName, // Name for value in the recipe
-			  psMetadata *arguments,  // Command-line arguments
-			  const char *argName	    // Argument name in the command-line arguments
+                          psMetadata *options,    // Target to which to add value
+                          const char *recipeName, // Name for value in the recipe
+                          psMetadata *arguments,  // Command-line arguments
+                          const char *argName       // Argument name in the command-line arguments
     );
 
@@ -198,3 +198,12 @@
 bool ppSimDefinePixels (psArray *sources, pmReadout *readout, psMetadata *recipe);
 
+/// Return software version
+psString ppSimVersion(void);
+
+/// Return software source
+psString ppSimSource(void);
+
+/// Return long version information
+psString ppSimVersionLong(void);
+
 #endif
Index: /branches/cnb_branches/cnb_branch_20090301/ppSim/src/ppSimBadPixels.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppSim/src/ppSimBadPixels.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ppSim/src/ppSimBadPixels.c	(revision 23352)
@@ -74,5 +74,5 @@
     }
 
-    psRandom *pseudoRNG = psRandomAlloc(PS_RANDOM_TAUS, seed); // Pseudo-random number generator
+    psRandom *pseudoRNG = psRandomAllocSpecific(PS_RANDOM_TAUS, seed); // Pseudo-random number generator
 
     psImage *image = readout->image;    // Image of interest
Index: /branches/cnb_branches/cnb_branch_20090301/ppSim/src/ppSimLoop.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppSim/src/ppSimLoop.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ppSim/src/ppSimLoop.c	(revision 23352)
@@ -25,5 +25,5 @@
     psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, PPSIM_RECIPE); // Recipe
 
-    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 0); // Random number generator
+    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS); // Random number generator
 
     char *typeStr = psMetadataLookupStr(NULL, recipe, "IMAGE.TYPE"); // Type of image to simulate
Index: /branches/cnb_branches/cnb_branch_20090301/ppSim/src/ppSimSequence.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppSim/src/ppSimSequence.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ppSim/src/ppSimSequence.c	(revision 23352)
@@ -133,12 +133,10 @@
     if (ppsim_recipe) psStringAppend (&ppSimCommand, " -recipe PPSIM %s", ppsim_recipe);
 
-    unsigned long seed = psMetadataLookupS32 (&status, config, "RND_SEED");
-    if (!status) seed = 0;
-    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, seed);
+    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS);
 
     psMetadataItem *item = psMetadataLookup (config, "SEQUENCE");
     if (item == NULL) {
         psLogMsg ("ppSimSequence", PS_LOG_WARN, "missing SEQUENCE description");
-        exit (1);
+        exit (PS_EXIT_CONFIG_ERROR);
     }
 
@@ -170,14 +168,14 @@
         }
 
-	// determine the camera for the sequence and define the ppSim command
-	if (camera == NULL) {
-	    camera = psMetadataLookupStr (&status, sequence, "CAMERA");
-	}
-
-	psString injectCommandReal = NULL;
-	psString ppSimCommandReal = NULL;
-
-	psStringAppend (&injectCommandReal, "%s --camera %s", injectCommand, camera);
-	psStringAppend (&ppSimCommandReal, "%s -camera %s", ppSimCommand, camera);
+        // determine the camera for the sequence and define the ppSim command
+        if (camera == NULL) {
+            camera = psMetadataLookupStr (&status, sequence, "CAMERA");
+        }
+
+        psString injectCommandReal = NULL;
+        psString ppSimCommandReal = NULL;
+
+        psStringAppend (&injectCommandReal, "%s --camera %s", injectCommand, camera);
+        psStringAppend (&ppSimCommandReal, "%s -camera %s", ppSimCommand, camera);
 
         if (!strcasecmp (type, "BIAS")) {
Index: /branches/cnb_branches/cnb_branch_20090301/ppSim/src/ppSimVersion.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppSim/src/ppSimVersion.c	(revision 23352)
+++ /branches/cnb_branches/cnb_branch_20090301/ppSim/src/ppSimVersion.c	(revision 23352)
@@ -0,0 +1,44 @@
+#include "ppSim.h"
+
+#ifndef PPSIM_VERSION
+#error "PPSIM_VERSION is not set"
+#endif
+#ifndef PPSIM_BRANCH
+#error "PPSIM_BRANCH is not set"
+#endif
+#ifndef PPSIM_SOURCE
+#error "PPSIM_SOURCE is not set"
+#endif
+
+#define xstr(s) str(s)
+#define str(s) #s
+
+psString ppSimVersion(void)
+{
+    char *value = NULL;
+    psStringAppend(&value, "%s@%s", xstr(PPSIM_BRANCH), xstr(PPSIM_VERSION));
+    return value;
+}
+
+psString ppSimSource(void)
+{
+    return psStringCopy(xstr(PPSIM_SOURCE));
+}
+
+psString ppSimVersionLong(void)
+{
+    psString version = ppSimVersion();  // Version, to return
+    psString source = ppSimSource();    // Source
+
+    psStringPrepend(&version, "ppSim ");
+    psStringAppend(&version, " from %s, built %s, %s", source, __DATE__, __TIME__);
+    psFree(source);
+
+#ifdef __OPTIMIZE__
+    psStringAppend(&version, " optimised");
+#else
+    psStringAppend(&version, " unoptimised");
+#endif
+
+    return version;
+};
Index: /branches/cnb_branches/cnb_branch_20090301/ppStack/src/Makefile.am
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppStack/src/Makefile.am	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ppStack/src/Makefile.am	(revision 23352)
@@ -1,5 +1,14 @@
 bin_PROGRAMS = ppStack
 
-ppStack_CFLAGS 	= $(PSLIB_CFLAGS) $(PSMODULE_CFLAGS) $(PSPHOT_CFLAGS) $(PPSTATS_CFLAGS) $(PPSTACK_CFLAGS)
+# PPSTACK_VERSION=`if [ -e ../../VERSION ]; then cat ../../VERSION; else svnversion; fi`
+# PPSTACK_BRANCH=`if [ -e ../../BRANCH ]; then cat ../../BRANCH; else svn info | sed -n -e '/URL:/ h' -e '/Repository Root:/ { x; H; x; s|Repository Root: \(.*\)\nURL: \1\(.*\)|\2| ; s|^/|| ; s|/[a-zA-Z]*/src.*|| ; p }'; fi`
+# PPSTACK_SOURCE=`if [ -e ../../SOURCE ]; then cat ../../SOURCE; else svn info | sed -n -e 's/Repository UUID: // p'; fi`
+# 
+# # Force recompilation of ppStackVersion.c, since it gets the version information
+# ppStackVersion.c: FORCE
+# 	touch ppStackVersion.c
+# FORCE: ;
+
+ppStack_CFLAGS 	= $(PSLIB_CFLAGS) $(PSMODULE_CFLAGS) $(PSPHOT_CFLAGS) $(PPSTATS_CFLAGS) $(PPSTACK_CFLAGS) -DPPSTACK_VERSION=$(SVN_VERSION) -DPPSTACK_BRANCH=$(SVN_BRANCH) -DPPSTACK_SOURCE=$(SVN_SOURCE)
 ppStack_LDFLAGS = $(PSLIB_LIBS)   $(PSMODULE_LIBS)   $(PSPHOT_LIBS)   $(PPSTATS_LIBS)   $(PPSTACK_LIBS)
 
@@ -8,15 +17,28 @@
 	ppStackArguments.c	\
 	ppStackCamera.c		\
+	ppStackFiles.c		\
 	ppStackLoop.c		\
 	ppStackPSF.c		\
 	ppStackReadout.c	\
-	ppStackPhotometry.c	\
 	ppStackVersion.c	\
 	ppStackMatch.c		\
 	ppStackSources.c	\
-	ppStackThread.c
+	ppStackThread.c		\
+	ppStackOptions.c	\
+	ppStackSetup.c		\
+	ppStackPrepare.c	\
+	ppStackConvolve.c	\
+	ppStackCombineInitial.c	\
+	ppStackReject.c		\
+	ppStackCombineFinal.c	\
+	ppStackCleanup.c	\
+	ppStackPhotometry.c	\
+	ppStackFinish.c
 
 noinst_HEADERS = 		\
-	ppStack.h
+	ppStack.h		\
+	ppStackLoop.h		\
+	ppStackOptions.h	\
+	ppStackThread.h
 
 CLEANFILES = *~
Index: /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStack.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStack.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStack.c	(revision 23352)
@@ -9,4 +9,5 @@
 
 #include "ppStack.h"
+#include "ppStackLoop.h"
 
 #define TIMER_NAME "PPSTACK"            // Name of timer
@@ -33,4 +34,6 @@
         goto die;
     }
+
+    ppStackVersionPrint();
 
     if (!pmModelClassInit()) {
@@ -74,5 +77,5 @@
     pmConfigDone();
     psLibFinalize();
-
+    pmVisualClose();
     exit(exitValue);
 }
Index: /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStack.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStack.h	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStack.h	(revision 23352)
@@ -1,4 +1,4 @@
-#ifndef PP_STACK_H
-#define PP_STACK_H
+#ifndef PPSTACK_H
+#define PPSTACK_H
 
 #define PPSTACK_RECIPE "PPSTACK"        // Name of the recipe
@@ -10,54 +10,20 @@
 // Mask values for inputs
 typedef enum {
-    PPSTACK_MASK_MATCH  = 0x01,         // PSF-matching failed
-    PPSTACK_MASK_CHI2   = 0x02,         // Chi^2 too deviant
-    PPSTACK_MASK_REJECT = 0x04,         // Rejection failed
-    PPSTACK_MASK_BAD    = 0x08,         // Bad image (too many pixels rejected)
+    PPSTACK_MASK_CAL    = 0x01,         // Photometric calibration failed
+    PPSTACK_MASK_MATCH  = 0x02,         // PSF-matching failed
+    PPSTACK_MASK_CHI2   = 0x04,         // Chi^2 too deviant
+    PPSTACK_MASK_REJECT = 0x08,         // Rejection failed
+    PPSTACK_MASK_BAD    = 0x10,         // Bad image (too many pixels rejected)
     PPSTACK_MASK_ALL    = 0xff          // All errors
 } ppStackMask;
 
-// Thread for stacking chunks
-//
-// Each input file contributes a readout, into which is read a chunk from that file
-typedef struct {
-    psArray *readouts;                  // Input readouts to read and stack
-    bool read;                          // Has the scan been read?
-    bool busy;                          // Is the scan being processed?
-    int firstScan;                      // First row of the chunk to be read for this group
-    int lastScan;                       // Last row of the chunk to be read for this group
-} ppStackThread;
+// List of files
+typedef enum {
+    PPSTACK_FILES_PREPARE,              // Files for preparation
+    PPSTACK_FILES_CONVOLVE,             // Files for convolution
+    PPSTACK_FILES_COMBINE,              // Files for combination
+    PPSTACK_FILES_PHOT                  // Files for photometry
+} ppStackFileList;
 
-// Allocator
-ppStackThread *ppStackThreadAlloc(psArray *readouts // Inputs readouts to read and stack
-    );
-
-// Data for threads
-typedef struct {
-    psArray *threads;                   // Threads doing stacking
-    int lastScan;                       // Last row that's been read
-    psArray *imageFits;                 // FITS file pointers for images
-    psArray *maskFits;                  // FITS file pointers for masks
-    psArray *varianceFits;                // FITS file pointers for variances
-} ppStackThreadData;
-
-// Set up thread data
-ppStackThreadData *ppStackThreadDataSetup(const psArray *cells, // Array of input cells
-                                          const psArray *imageNames, // Names of images to read
-                                          const psArray *maskNames, // Names of masks to read
-                                          const psArray *varianceNames, // Names of variance maps to read
-                                          const psArray *covariances, // Covariance matrices (already read)
-                                          const pmConfig *config // Configuration
-    );
-
-// Read chunk into the first available file thread
-ppStackThread *ppStackThreadRead(bool *status, // Status of read
-                                 ppStackThreadData *stack, // Stacks available for reading
-                                 pmConfig *config, // Configuration
-                                 int numChunk, // Chunk number (only for interest)
-                                 int overlap // Overlap between subsequent scans
-    );
-
-// Initialise the threads
-void ppStackThreadInit(void);
 
 // Setup command-line arguments
@@ -74,12 +40,9 @@
     );
 
-// Loop over the inputs, doing the combination
-bool ppStackLoop(pmConfig *config       // Configuration
-    );
-
 // Determine target PSF for input images
 pmPSF *ppStackPSF(const pmConfig *config, // Configuration
                   int numCols, int numRows, // Size of image
-                  const psArray *psfs   // List of input PSFs
+                  const psArray *psfs,  // List of input PSFs
+                  const psVector *inputMask // Mask for inputs
     );
 
@@ -125,19 +88,19 @@
     );
 
-// Perform photometry on stack
-bool ppStackPhotometry(pmConfig *config, // Configuration
-                       const pmReadout *readout, // Readout to be photometered
-                       const pmFPAview *view // View to readout
-    );
-
 // Return software version
 psString ppStackVersion(void);
+
+/// Return software source
+psString ppStackSource(void);
 
 // Return long description of software version
 psString ppStackVersionLong(void);
 
-// Supplement metadata with software version
-void ppStackVersionMetadata(psMetadata *metadata // Metadata to supplement
+// Supplement header with software version
+bool ppStackVersionHeader(psMetadata *header // Header to supplement
     );
+
+/// Print version information
+void ppStackVersionPrint(void);
 
 /// Convolve image to match specified seeing
@@ -158,7 +121,51 @@
 /// Corrects the source PSF photometry to a common system.  Return the sum of the exposure times.
 float ppStackSourcesTransparency(const psArray *sourceLists, // Sources for each input
+                                 psVector *inputMask, // Indicates bad input
                                  const pmFPAview *view, // View to readout
                                  const pmConfig *config // Configuration
     );
 
+/// Dump memory debugging information
+void ppStackMemDump(
+    const char *name                    ///< Stage name, for inclusion in the output file name
+    );
+
+/// Activate/deactivate a list of files
+void ppStackFileActivation(
+    pmConfig *config,                   // Configuration
+    ppStackFileList list,               // Files to turn on/off
+    bool state                          // Activation state
+    );
+
+// Activate/deactivate a single element for a list
+void ppStackFileActivationSingle(
+    pmConfig *config,                   // Configuration
+    ppStackFileList list,               // Files to turn on/off
+    bool state,                         // Activation state
+    int num                             // Number of file in sequence
+    );
+
+/// Iterate down the hierarchy, loading files
+///
+/// We can get away with this simplistic treatment of the FPA hierarchy because we're working on skycells.
+pmFPAview *ppStackFilesIterateDown(
+    pmConfig *config                    // Configuration
+    );
+
+/// Iterate up the hierarchy, writing files
+///
+/// We can get away with this simplistic treatment of the FPA hierarchy because we're working on skycells.
+bool ppStackFilesIterateUp(
+    pmConfig *config                    // Configuration
+    );
+
+/// Write an image to a FITS file
+bool ppStackWriteImage(
+    const char *name,                   // Name of image
+    psMetadata *header,                 // Header
+    const psImage *image,               // Image
+    pmConfig *config                    // Configuration
+    );
+
+
 #endif
Index: /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackArguments.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackArguments.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackArguments.c	(revision 23352)
@@ -168,12 +168,15 @@
                       "Play safe with small numbers of pixels to combine?", false);
     psMetadataAddF32(arguments, PS_LIST_TAIL, "-zp-radius", 0, "Radius (pixels) for matching sources", NAN);
-    psMetadataAddS32(arguments, PS_LIST_TAIL, "-zp-iter", 0, "Maximum iterations for zero point", 0);
+    psMetadataAddS32(arguments, PS_LIST_TAIL, "-zp-iter-1", 0, "Maximum iterations for zero point; pass 1", 0);
+    psMetadataAddS32(arguments, PS_LIST_TAIL, "-zp-iter-2", 0, "Maximum iterations for zero point; pass 2", 0);
     psMetadataAddF32(arguments, PS_LIST_TAIL, "-zp-tol", 0, "Tolerance for zero point iterations", NAN);
     psMetadataAddS32(arguments, PS_LIST_TAIL, "-zp-trans-iter", 0, "Iterations for transparency determination", 0);
     psMetadataAddF32(arguments, PS_LIST_TAIL, "-zp-trans-rej", 0, "Rejection threshold for transparency determination", NAN);
     psMetadataAddF32(arguments, PS_LIST_TAIL, "-zp-trans-thresh", 0, "Threshold for transparency determination", NAN);
-    psMetadataAddF32(arguments, PS_LIST_TAIL, "-zp-star-rej", 0, "Rejection threshold for stars", NAN);
+    psMetadataAddF32(arguments, PS_LIST_TAIL, "-zp-star-rej-1", 0, "Rejection threshold for stars; pass 1", NAN);
+    psMetadataAddF32(arguments, PS_LIST_TAIL, "-zp-star-rej-2", 0, "Rejection threshold for stars; pass 2", NAN);
     psMetadataAddF32(arguments, PS_LIST_TAIL, "-zp-star-limit", 0, "Limit on star rejection fraction for successful iteration", NAN);
-    psMetadataAddF32(arguments, PS_LIST_TAIL, "-zp-star-sys", 0, "Estimated systematic error", NAN);
+    psMetadataAddF32(arguments, PS_LIST_TAIL, "-zp-star-sys-1", 0, "Estimated systematic error; pass 1", NAN);
+    psMetadataAddF32(arguments, PS_LIST_TAIL, "-zp-star-sys-2", 0, "Estimated systematic error; pass 2", NAN);
     psMetadataAddStr(arguments, PS_LIST_TAIL, "-temp-image", 0, "Suffix for temporary images", NULL);
     psMetadataAddStr(arguments, PS_LIST_TAIL, "-temp-mask", 0, "Suffix for temporary masks", NULL);
@@ -182,5 +185,5 @@
                       "Delete temporary files on completion?", false);
     psMetadataAddS32(arguments, PS_LIST_TAIL, "-threads", 0, "Number of threads to use", 0);
-    psMetadataAddBool(arguments, PS_LIST_TAIL, "-psphot-visual", 0, "psphot visualisation", 0);
+    psMetadataAddBool(arguments, PS_LIST_TAIL, "-visual", 0, "visualisation", false);
 
     if (argc == 1 || !psArgumentParse(arguments, &argc, argv) || argc != 3) {
@@ -242,12 +245,15 @@
 
     VALUE_ARG_RECIPE_FLOAT("-zp-radius",       "ZP.RADIUS",       F32);
-    VALUE_ARG_RECIPE_INT("-zp-iter",           "ZP.ITER",         S32, 0);
+    VALUE_ARG_RECIPE_INT("-zp-iter-1",         "ZP.ITER.1",       S32, 0);
+    VALUE_ARG_RECIPE_INT("-zp-iter-2",         "ZP.ITER.2",       S32, 0);
     VALUE_ARG_RECIPE_FLOAT("-zp-tol",          "ZP.TOL",          F32);
     VALUE_ARG_RECIPE_INT("-zp-trans-iter",     "ZP.TRANS.ITER",   S32, 0);
     VALUE_ARG_RECIPE_FLOAT("-zp-trans-rej",    "ZP.TRANS.REJ",    F32);
     VALUE_ARG_RECIPE_FLOAT("-zp-trans-thresh", "ZP.TRANS.THRESH", F32);
-    VALUE_ARG_RECIPE_FLOAT("-zp-star-rej",     "ZP.STAR.REJ",     F32);
+    VALUE_ARG_RECIPE_FLOAT("-zp-star-rej-1",   "ZP.STAR.REJ.1",   F32);
+    VALUE_ARG_RECIPE_FLOAT("-zp-star-rej-2",   "ZP.STAR.REJ.2",   F32);
     VALUE_ARG_RECIPE_FLOAT("-zp-star-limit",   "ZP.STAR.LIMIT",   F32);
-    VALUE_ARG_RECIPE_FLOAT("-zp-star-sys",     "ZP.STAR.SYS",     F32);
+    VALUE_ARG_RECIPE_FLOAT("-zp-star-sys-1",   "ZP.STAR.SYS.1",   F32);
+    VALUE_ARG_RECIPE_FLOAT("-zp-star-sys-2",   "ZP.STAR.SYS.2",   F32);
 
     VALUE_ARG_RECIPE_INT("-psf-instances", "PSF.INSTANCES", S32, 0);
@@ -255,4 +261,8 @@
     VALUE_ARG_RECIPE_INT("-psf-order",     "PSF.ORDER",     S32, 0);
     valueArgRecipeStr(arguments, recipe, "-psf-model", "PSF.MODEL", recipe);
+
+    if (psMetadataLookupBool(NULL, arguments, "-visual")) {
+        pmVisualSetVisual(true);
+    }
 
     if (psMetadataLookupBool(NULL, arguments, "-photometry") ||
@@ -290,5 +300,5 @@
     if (dump_file) {
         pmConfigCamerasCull(config, NULL);
-        pmConfigRecipesCull(config, "PPSTACK,PPSUB,PPSTATS,PSPHOT,MASKS");
+        pmConfigRecipesCull(config, "PPSTACK,PPSUB,PPSTATS,PSPHOT,MASKS,JPEG");
 
         pmFPAfile *input = psMetadataLookupPtr(NULL, config->files, "PPSTACK.INPUT"); // Input file
Index: /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackCleanup.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackCleanup.c	(revision 23352)
+++ /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackCleanup.c	(revision 23352)
@@ -0,0 +1,130 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <pslib.h>
+#include <psmodules.h>
+
+#include "ppStack.h"
+#include "ppStackLoop.h"
+
+#define WCS_TOLERANCE 0.001             // Tolerance for WCS
+
+
+bool ppStackCleanup(ppStackThreadData *stack, ppStackOptions *options, pmConfig *config)
+{
+    psAssert(stack, "Require stack");
+    psAssert(options, "Require options");
+    psAssert(config, "Require configuration");
+
+    psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, PPSTACK_RECIPE); // ppStack recipe
+    psAssert(recipe, "We've thrown an error on this before.");
+
+    bool mdok;                          // Status of MD lookup
+    bool tempDelete = psMetadataLookupBool(&mdok, recipe, "TEMP.DELETE"); // Delete temporary files?
+
+#if 0
+    // Ensure masked regions really look masked
+    {
+        psString maskBadStr = psMetadataLookupStr(NULL, recipe, "MASK.BAD"); // Name of bits for bad
+        psImageMaskType maskBad = pmConfigMaskGet(maskBadStr, config); // Bits to mask for bad pixels
+        if (!pmReadoutMaskApply(options->outRO, maskBad)) {
+            psWarning("Unable to apply mask");
+        }
+    }
+#endif
+
+    // Close up
+    bool wcsDone = false;           // Have we done the WCS?
+    for (int i = 0; i < options->num; i++) {
+        if (options->inputMask->data.PS_TYPE_VECTOR_MASK_DATA[i]) {
+            continue;
+        }
+
+        ppStackThread *thread = stack->threads->data[0]; // Representative stack
+        pmReadout *inRO = thread->readouts->data[i]; // Template readout
+        if (inRO && !wcsDone) {
+            // Copy astrometry over
+            wcsDone = true;
+            pmHDU *inHDU = pmHDUFromCell(inRO->parent); // Template HDU
+            pmHDU *outHDU = pmHDUFromCell(options->outRO->parent); // Output HDU
+            pmChip *outChip = options->outRO->parent->parent; // Output chip
+            pmFPA *outFPA = outChip->parent; // Output FPA
+            if (!outHDU || !inHDU) {
+                psWarning("Unable to find HDU at FPA level to copy astrometry.");
+            } else {
+                if (!pmAstromReadWCS(outFPA, outChip, inHDU->header, 1.0)) {
+                    psErrorClear();
+                    psWarning("Unable to read WCS astrometry from input FPA.");
+                    wcsDone = false;
+                } else {
+                    if (!outHDU->header) {
+                        outHDU->header = psMetadataAlloc();
+                    }
+                    if (!pmAstromWriteWCS(outHDU->header, outFPA, outChip, WCS_TOLERANCE)) {
+                        psErrorClear();
+                        psWarning("Unable to write WCS astrometry to output FPA.");
+                        wcsDone = false;
+                    }
+                }
+            }
+        }
+
+        if (tempDelete) {
+            psString imageResolved = pmConfigConvertFilename(options->imageNames->data[i],
+                                                             config, false, false);
+            psString maskResolved = pmConfigConvertFilename(options->maskNames->data[i],
+                                                            config, false, false);
+            psString varianceResolved = pmConfigConvertFilename(options->varianceNames->data[i],
+                                                                config, false, false);
+            if (unlink(imageResolved) == -1 || unlink(maskResolved) == -1 ||
+                unlink(varianceResolved) == -1) {
+                psWarning("Unable to delete temporary files for image %d", i);
+            }
+            psFree(imageResolved);
+            psFree(maskResolved);
+            psFree(varianceResolved);
+        }
+    }
+
+    ppStackMemDump("cleanup");
+
+    // Generate binned JPEGs
+    {
+        int bin1 = psMetadataLookupS32(NULL, recipe, "BIN1"); // First binning level
+        int bin2 = psMetadataLookupS32(NULL, recipe, "BIN2"); // Second binning level
+
+        // Target cells
+        pmFPAview *view = pmFPAviewAlloc(0); // View to cells of interest
+        view->chip = view->cell = 0;
+        pmCell *cell1 = pmFPAfileThisCell(config->files, view, "PPSTACK.OUTPUT.JPEG1");
+        pmCell *cell2 = pmFPAfileThisCell(config->files, view, "PPSTACK.OUTPUT.JPEG2");
+        psImageMaskType maskValue = pmConfigMaskGet("BLANK", config); // Bits to mask
+
+        pmReadout *ro1 = pmReadoutAlloc(cell1), *ro2 = pmReadoutAlloc(cell2); // Binned readouts
+        if (!pmReadoutRebin(ro1, options->outRO, maskValue, bin1, bin1) ||
+            !pmReadoutRebin(ro2, ro1, 0, bin2, bin2)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to bin output.");
+            psFree(ro1);
+            psFree(ro2);
+            return false;
+        }
+        psFree(ro1);
+        psFree(ro2);
+    }
+
+    // Put version information into the header
+    pmHDU *hdu = pmHDUFromCell(options->outRO->parent);
+    if (!hdu) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to find HDU for output.");
+        return false;
+    }
+    if (!hdu->header) {
+        hdu->header = psMetadataAlloc();
+    }
+    ppStackVersionHeader(hdu->header);
+
+    return true;
+}
Index: /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackCombineFinal.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackCombineFinal.c	(revision 23352)
+++ /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackCombineFinal.c	(revision 23352)
@@ -0,0 +1,55 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <pslib.h>
+#include <psmodules.h>
+
+#include "ppStack.h"
+#include "ppStackLoop.h"
+
+bool ppStackCombineFinal(ppStackThreadData *stack, ppStackOptions *options, pmConfig *config)
+{
+    psAssert(stack, "Require stack");
+    psAssert(options, "Require options");
+    psAssert(config, "Require configuration");
+
+    stack->lastScan = 0;            // Reset read
+    bool status;                    // Status of read
+    for (int numChunk = 0; true; numChunk++) {
+        ppStackThread *thread = ppStackThreadRead(&status, stack, config, numChunk, 0);
+        if (!status) {
+            // Something went wrong
+            psError(PS_ERR_UNKNOWN, false, "Unable to read chunk %d", numChunk);
+            return false;
+        }
+        if (!thread) {
+            // Nothing more to read
+            break;
+        }
+
+        // call: ppStackReadoutFinal(config, outRO, readouts, rejected)
+        psThreadJob *job = psThreadJobAlloc("PPSTACK_FINAL_COMBINE"); // Job to start
+        psArrayAdd(job->args, 1, thread);
+        psArrayAdd(job->args, 1, options);
+        psArrayAdd(job->args, 1, config);
+        if (!psThreadJobAddPending(job)) {
+            psFree(job);
+            return false;
+        }
+        psFree(job);
+    }
+
+    if (!psThreadPoolWait(true)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to do final combination.");
+        return false;
+    }
+
+#ifdef TESTING
+    pmStackVisualPlotTestImage(outRO->image, "combined_initial.fits");
+    ppStackWriteImage("combined_final.fits", NULL, outRO->image, config);
+#endif
+
+    return true;
+}
Index: /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackCombineInitial.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackCombineInitial.c	(revision 23352)
+++ /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackCombineInitial.c	(revision 23352)
@@ -0,0 +1,135 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <pslib.h>
+#include <psmodules.h>
+
+#include "ppStack.h"
+#include "ppStackLoop.h"
+
+bool ppStackCombineInitial(ppStackThreadData *stack, ppStackOptions *options, pmConfig *config)
+{
+    psAssert(stack, "Require stack");
+    psAssert(options, "Require options");
+    psAssert(config, "Require configuration");
+
+    psTimerStart("PPSTACK_FINAL");
+
+    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
+    int overlap = 2 * psMetadataLookupS32(NULL, ppsub,
+                                          "KERNEL.SIZE"); // Overlap by kernel size between consecutive scans
+
+    pmFPAview *view = NULL;             // View to readout
+
+    int row0, col0;                 // Offset for readout
+    int numCols, numRows;           // Size of readout
+    ppStackThread *thread = stack->threads->data[0]; // Representative thread
+    if (!pmReadoutStackSetOutputSize(&col0, &row0, &numCols, &numRows, thread->readouts)) {
+        psError(PS_ERR_UNKNOWN, false, "problem setting output readout size.");
+        return false;
+    }
+
+    pmFPAfileActivate(config->files, false, NULL);
+    ppStackFileActivation(config, PPSTACK_FILES_COMBINE, true);
+    view = ppStackFilesIterateDown(config);
+    if (!view) {
+        return false;
+    }
+
+    pmCell *outCell = pmFPAfileThisCell(config->files, view, "PPSTACK.OUTPUT"); // Output cell
+    options->outRO = pmReadoutAlloc(outCell); // Output readout
+    psFree(view);
+
+    psString maskBadStr = psMetadataLookupStr(NULL, recipe, "MASK.BAD"); // Name of bits to mask for bad
+    psImageMaskType maskBad = pmConfigMaskGet(maskBadStr, config); // Bits to mask for bad pixels
+    if (!pmReadoutStackDefineOutput(options->outRO, col0, row0, numCols, numRows, true, true, maskBad)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to prepare output.");
+        return false;
+    }
+
+    psFree(options->cells); options->cells = NULL;
+
+    bool status;                    // Status of read
+    int numChunk;                   // Number of chunks
+    for (numChunk = 0; true; numChunk++) {
+        ppStackThread *thread = ppStackThreadRead(&status, stack, config, numChunk, overlap);
+        if (!status) {
+            // Something went wrong
+            psError(PS_ERR_UNKNOWN, false, "Unable to read chunk %d", numChunk);
+            return false;
+        }
+        if (!thread) {
+            // Nothing more to read
+            break;
+        }
+
+        // call: ppStackReadoutInitial(config, outRO, readouts, subRegions, subKernels)
+        psThreadJob *job = psThreadJobAlloc("PPSTACK_INITIAL_COMBINE"); // Job to start
+        psArrayAdd(job->args, 1, thread);
+        psArrayAdd(job->args, 1, options);
+        psArrayAdd(job->args, 1, config);
+        if (!psThreadJobAddPending(job)) {
+            psFree(job);
+            return false;
+        }
+        psFree(job);
+    }
+
+    if (!psThreadPoolWait(false)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to do initial combination.");
+        return false;
+    }
+
+    // Harvest the jobs, gathering the inspection lists
+    options->inspect = psArrayAlloc(options->num);
+    for (int i = 0; i < options->num; i++) {
+        if (options->inputMask->data.PS_TYPE_VECTOR_MASK_DATA[i]) {
+            continue;
+        }
+        options->inspect->data[i] = psArrayAllocEmpty(numChunk);
+    }
+    psThreadJob *job;               // Completed job
+    while ((job = psThreadJobGetDone())) {
+        psAssert(strcmp(job->type, "PPSTACK_INITIAL_COMBINE") == 0,
+                 "Job has incorrect type: %s", job->type);
+        psArray *results = job->results; // Results of job
+        for (int i = 0; i < options->num; i++) {
+            if (options->inputMask->data.PS_TYPE_VECTOR_MASK_DATA[i]) {
+                continue;
+            }
+            options->inspect->data[i] = psArrayAdd(options->inspect->data[i], 1, results->data[i]);
+        }
+        psFree(job);
+    }
+
+    ppStackMemDump("initial");
+
+#ifdef TESTING
+    ppStackWriteImage("combined_initial.fits", NULL, outRO->image, config);
+    pmStackVisualPlotTestImage(outRO->image, "combined_initial.fits");
+#endif
+
+    // Sum covariance matrices
+    for (int i = 0; i < options->num; i++) {
+        if (options->inputMask->data.U8[i]) {
+            psFree(options->covariances->data[i]);
+            options->covariances->data[i] = NULL;
+        }
+    }
+    options->outRO->covariance = psImageCovarianceSum(options->covariances);
+    psFree(options->covariances); options->covariances = NULL;
+    psFree(options->matchChi2); options->matchChi2 = NULL;
+
+
+    if (options->stats) {
+        psMetadataAddF32(options->stats, PS_LIST_TAIL, "TIME_FINAL", 0,
+                         "Time to make final stack", psTimerMark("PPSTACK_FINAL"));
+    }
+
+    return true;
+}
Index: /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackConvolve.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackConvolve.c	(revision 23352)
+++ /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackConvolve.c	(revision 23352)
@@ -0,0 +1,238 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <pslib.h>
+#include <psmodules.h>
+
+#include "ppStack.h"
+#include "ppStackLoop.h"
+
+// Update the value of a concept
+#define UPDATE_CONCEPT(SOURCE, NAME, VALUE) { \
+    psMetadataItem *item = psMetadataLookup(SOURCE->concepts, NAME); \
+    psAssert(item, "Concept should be present"); \
+    psAssert(item->type == PS_TYPE_F32, "Concept should be F32"); \
+    item->data.F32 = VALUE; \
+}
+
+
+
+bool ppStackConvolve(ppStackOptions *options, pmConfig *config)
+{
+    psAssert(options, "Require options");
+    psAssert(config, "Require configuration");
+
+    psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, PPSTACK_RECIPE); // ppStack recipe
+    psAssert(recipe, "We've thrown an error on this before.");
+
+    int num = options->num;             // Number of inputs
+    options->cells = psArrayAlloc(num); // Cells for convolved images --- a handle for reading again
+    options->subKernels = psArrayAlloc(num); // Subtraction kernels --- required in the stacking
+    options->subRegions = psArrayAlloc(num); // Subtraction regions --- required in the stacking
+    int numGood = 0;                    // Number of good frames
+    options->numCols = 0;
+    options->numRows = 0;
+    options->matchChi2 = psVectorAlloc(num, PS_TYPE_F32); // chi^2 for stamps when matching
+    psVectorInit(options->matchChi2, NAN);
+    options->weightings = psVectorAlloc(num, PS_TYPE_F32); // Combination weightings for images (1/noise^2)
+    psVectorInit(options->weightings, NAN);
+    options->covariances = psArrayAlloc(num); // Covariance matrices
+
+    psList *fpaList = psListAlloc(NULL); // List of input FPAs, for concept averaging
+    psList *cellList = psListAlloc(NULL); // List of input cells, for concept averaging
+    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS); // Random number generator
+
+    for (int i = 0; i < num; i++) {
+        if (options->inputMask->data.U8[i]) {
+            continue;
+        }
+        psTrace("ppStack", 2, "Convolving input %d of %d to target PSF....\n", i, num);
+        pmFPAfileActivate(config->files, false, NULL);
+        ppStackFileActivationSingle(config, PPSTACK_FILES_CONVOLVE, true, i);
+        pmFPAfile *file = pmFPAfileSelectSingle(config->files, "PPSTACK.INPUT", i); // File of interest
+        pmFPAview *view = ppStackFilesIterateDown(config);
+        if (!view) {
+            psFree(rng);
+            psFree(fpaList);
+            psFree(cellList);
+            return false;
+        }
+
+        pmReadout *readout = pmFPAviewThisReadout(view, file->fpa); // Input readout
+        psFree(view);
+
+        if (options->numCols == 0 && options->numRows == 0) {
+            options->numCols = readout->image->numCols;
+            options->numRows = readout->image->numRows;
+        } else if (options->numCols != readout->image->numCols ||
+                   options->numRows != readout->image->numRows) {
+            psError(PS_ERR_UNKNOWN, true, "Sizes of input images don't match: %dx%d vs %dx%d",
+                    readout->image->numCols, readout->image->numRows, options->numCols, options->numRows);
+            psFree(rng);
+            psFree(fpaList);
+            psFree(cellList);
+            return false;
+        }
+
+        // Background subtraction, scaling and normalisation is performed automatically by the image matching
+        psArray *regions = NULL, *kernels = NULL; // Regions and kernels used in subtraction
+        psTimerStart("PPSTACK_MATCH");
+
+        if (!ppStackMatch(readout, &regions, &kernels, &options->matchChi2->data.F32[i],
+                          &options->weightings->data.F32[i], options->sourceLists->data[i],
+                          options->psf, rng, config)) {
+            psErrorStackPrint(stderr, "Unable to match image %d --- ignoring.", i);
+            options->inputMask->data.PS_TYPE_VECTOR_MASK_DATA[i] = PPSTACK_MASK_MATCH;
+            psErrorClear();
+            continue;
+        }
+        options->covariances->data[i] = psMemIncrRefCounter(readout->covariance);
+
+        if (options->stats) {
+            pmSubtractionKernels *kernels = psMetadataLookupPtr(NULL, readout->analysis,
+                                                                PM_SUBTRACTION_ANALYSIS_KERNEL);// Conv kernel
+            psMetadataAddF32(options->stats, PS_LIST_TAIL, "TIME_MATCH", PS_META_DUPLICATE_OK,
+                             "Time to match PSF", psTimerMark("PPSTACK_MATCH"));
+            psMetadataAddF32(options->stats, PS_LIST_TAIL, "STAMP.MEAN", PS_META_DUPLICATE_OK,
+                             "Mean deviation for stamps", kernels->mean);
+            psMetadataAddF32(options->stats, PS_LIST_TAIL, "STAMP.RMS", PS_META_DUPLICATE_OK,
+                             "RMS deviation for stamps", kernels->rms);
+            psMetadataAddF32(options->stats, PS_LIST_TAIL, "STAMP.NUM", PS_META_DUPLICATE_OK,
+                             "Number of stamps", kernels->numStamps);
+            float deconv = psMetadataLookupF32(NULL, readout->analysis,
+                                               PM_SUBTRACTION_ANALYSIS_DECONV_MAX); // Deconvolution fraction
+            psMetadataAddF32(options->stats, PS_LIST_TAIL, "KERNEL.DECONV", PS_META_DUPLICATE_OK,
+                             "Deconvolution fraction for kernel", deconv);
+            psMetadataAddF32(options->stats, PS_LIST_TAIL, "PPSTACK.WEIGHTING", PS_META_DUPLICATE_OK,
+                             "Weighting for image", options->weightings->data.F32[i]);
+        }
+        psLogMsg("ppStack", PS_LOG_INFO, "Time to match image %d: %f sec", i, psTimerClear("PPSTACK_MATCH"));
+
+        options->subRegions->data[i] = regions;
+        options->subKernels->data[i] = kernels;
+
+        // Write the temporary convolved files
+        pmHDU *hdu = readout->parent->parent->parent->hdu; // HDU for convolved image
+        assert(hdu);
+        ppStackWriteImage(options->imageNames->data[i], hdu->header, readout->image, config);
+        psMetadata *maskHeader = psMetadataCopy(NULL, hdu->header); // Copy of header, for mask
+        pmConfigMaskWriteHeader(config, maskHeader);
+        ppStackWriteImage(options->maskNames->data[i], maskHeader, readout->mask, config);
+        psFree(maskHeader);
+        psImageCovarianceTransfer(readout->variance, readout->covariance);
+        ppStackWriteImage(options->varianceNames->data[i], hdu->header, readout->variance, config);
+#ifdef TESTING
+        {
+            psString name = NULL;
+            psStringAppend(&name, "covariance_%d.fits", i);
+            ppStackWriteImage(name, hdu->header, readout->covariance->image, config);
+            pmStackVisualPlotTestImage(readout->covariance->image, name);
+            psFree(name);
+        }
+#endif
+
+        pmCell *inCell = readout->parent; // Input cell
+
+        psListAdd(cellList, PS_LIST_TAIL, inCell);
+        psListAdd(fpaList, PS_LIST_TAIL, inCell->parent->parent);
+
+        options->cells->data[i] = psMemIncrRefCounter(inCell);
+        if (!ppStackFilesIterateUp(config)) {
+            psFree(fpaList);
+            psFree(cellList);
+            psFree(rng);
+            return false;
+        }
+        numGood++;
+
+        ppStackMemDump("match");
+    }
+    psFree(rng);
+
+    psFree(options->sourceLists); options->sourceLists = NULL;
+    psFree(options->psf); options->psf = NULL;
+
+
+    if (numGood == 0) {
+        psError(PS_ERR_UNKNOWN, false, "No good images to combine.");
+        psFree(fpaList);
+        psFree(cellList);
+        return false;
+    }
+
+    // Update concepts for output
+    // XXX This should probably be placed later, so that it's not influenced
+    // by images that get rejected later.
+    {
+        pmFPAview view;                 // View for output
+        view.chip = view.cell = view.readout = 0;
+        pmCell *outCell = pmFPAfileThisCell(config->files, &view, "PPSTACK.OUTPUT"); // Output cell
+        pmFPA *outFPA = outCell->parent->parent; // Output FPA
+        pmConceptsAverageFPAs(outFPA, fpaList);
+        pmConceptsAverageCells(outCell, cellList, NULL, NULL, true);
+        psFree(fpaList);
+        psFree(cellList);
+
+        UPDATE_CONCEPT(outFPA,  "FPA.EXPOSURE",  options->sumExposure);
+        UPDATE_CONCEPT(outCell, "CELL.EXPOSURE", options->sumExposure);
+        UPDATE_CONCEPT(outCell, "CELL.DARKTIME", NAN);
+    }
+
+
+    // Reject images out-of-hand on the basis of their match chi^2
+    {
+        psVector *values = psVectorAllocEmpty(num, PS_TYPE_F32); // Values to sort
+        for (int i = 0; i < num; i++) {
+            if (options->inputMask->data.PS_TYPE_VECTOR_MASK_DATA[i] & PPSTACK_MASK_ALL) {
+                continue;
+            }
+            values->data.F32[values->n++] = options->matchChi2->data.F32[i];
+        }
+        assert(values->n == numGood);
+        if (!psVectorSortInPlace(values)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to sort vector.");
+            psFree(values);
+            return false;
+        }
+        float median = numGood % 2 ? values->data.F32[numGood / 2] :
+            0.5 * (values->data.F32[numGood / 2 - 1] + values->data.F32[numGood / 2]);
+
+        float rms = 0.74 * (values->data.F32[numGood * 3 / 4] -
+                            values->data.F32[numGood / 4]); // Estimated RMS from interquartile range
+        psFree(values);
+
+        float rej = psMetadataLookupF32(NULL, recipe, "MATCH.REJ"); // Rejection threshold (stdevs)
+        if (isfinite(rej)) {
+            float thresh = median + rej * rms; // Threshold for rejection
+            psLogMsg("ppStack", PS_LOG_INFO, "chi^2 rejection threshold = %f + %f * %f = %f",
+                     median, rej, rms, thresh);
+
+            int numRej = 0;                 // Number rejected
+            numGood = 0;                    // Number of good images
+            for (int i = 0; i < num; i++) {
+              if (options->inputMask->data.PS_TYPE_VECTOR_MASK_DATA[i] & PPSTACK_MASK_ALL) {
+                    continue;
+                }
+                if (options->matchChi2->data.F32[i] > thresh) {
+                    numRej++;
+                    options->inputMask->data.PS_TYPE_VECTOR_MASK_DATA[i] |= PPSTACK_MASK_CHI2;
+                    psLogMsg("ppStack", PS_LOG_INFO, "Rejecting image %d because of large matching chi^2: %f",
+                             i, options->matchChi2->data.F32[i]);
+                } else {
+                    psLogMsg("ppStack", PS_LOG_INFO, "Image %d has matching chi^2: %f",
+                             i, options->matchChi2->data.F32[i]);
+                    numGood++;
+                }
+            }
+        }
+    }
+
+    if (numGood == 0) {
+        psError(PS_ERR_UNKNOWN, false, "No good images to combine.");
+        return false;
+    }
+
+    return true;
+}
Index: /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackFiles.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackFiles.c	(revision 23352)
+++ /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackFiles.c	(revision 23352)
@@ -0,0 +1,184 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <pslib.h>
+#include <psmodules.h>
+
+#include "ppStack.h"
+
+
+// Here follows lists of files for activation/deactivation at various stages.  Each must be NULL-terminated.
+
+/// Files required in preparation for convolution
+static char *filesPrepare[] = { "PPSTACK.INPUT.PSF", "PPSTACK.INPUT.SOURCES", "PPSTACK.TARGET.PSF", NULL };
+
+/// Files required for the convolution
+static char *filesConvolve[] = { "PPSTACK.INPUT", "PPSTACK.INPUT.MASK", "PPSTACK.INPUT.VARIANCE", NULL };
+
+/// Output files for the combination
+static char *filesCombine[] = { "PPSTACK.OUTPUT", "PPSTACK.OUTPUT.MASK", "PPSTACK.OUTPUT.VARIANCE",
+                                "PPSTACK.OUTPUT.JPEG1", "PPSTACK.OUTPUT.JPEG2", NULL };
+
+/// Files for photometry
+static char *filesPhot[] = { "PSPHOT.INPUT", "PSPHOT.OUTPUT", "PSPHOT.RESID", "PSPHOT.BACKMDL",
+                             "PSPHOT.BACKMDL.STDEV", "PSPHOT.BACKGND", "PSPHOT.BACKSUB",
+                             "SOURCE.PLOT.MOMENTS", "SOURCE.PLOT.PSFMODEL", "SOURCE.PLOT.APRESID",
+                             "PSPHOT.INPUT.CMF", NULL };
+
+static char **stackFiles(ppStackFileList list)
+{
+    switch (list) {
+      case PPSTACK_FILES_PREPARE:  return filesPrepare;
+      case PPSTACK_FILES_CONVOLVE: return filesConvolve;
+      case PPSTACK_FILES_COMBINE:  return filesCombine;
+      case PPSTACK_FILES_PHOT:     return filesPhot;
+      default:
+        psAbort("Unrecognised file list: %x", list);
+    }
+    return NULL;
+}
+
+
+void ppStackMemDump(const char *name)
+{
+    return;
+
+    static int num = 0;                 // Counter, to make files unique and give an idea of sequence
+
+    psString filename = NULL;           // Name of file
+    psStringAppend(&filename, "memdump_%s_%03d.txt", name, num);
+    FILE *memFile = fopen(filename, "w");
+    psFree(filename);
+
+    psMemBlock **leaks = NULL;
+    int numLeaks = psMemCheckLeaks(0, &leaks, NULL, true);
+    fprintf(memFile, "# MemBlock Size Source\n");
+    unsigned long total = 0;            // Total memory used
+    for (int i = 0; i < numLeaks; i++) {
+        psMemBlock *mb = leaks[i];
+        fprintf(memFile, "%12lu\t%12zd\t%s:%d\n", mb->id, mb->userMemorySize,
+                mb->file, mb->lineno);
+        total += mb->userMemorySize;
+    }
+    fclose(memFile);
+    psFree(leaks);
+
+    fprintf(stderr, "Memdump %s %d: Memory use: %ld, sbrk: %p\n", name, num, total, sbrk(0));
+    num++;
+}
+
+
+
+// Activate/deactivate a list of files
+void ppStackFileActivation(pmConfig *config, // Configuration
+                           ppStackFileList list, // Files to turn on/off
+                           bool state   // Activation state
+    )
+{
+    assert(config);
+
+    char **files = stackFiles(list);    // Files to turn on/off
+    for (int i = 0; files[i] != NULL; i++) {
+        pmFPAfileActivate(config->files, state, files[i]);
+    }
+    return;
+}
+
+// Activate/deactivate a single element for a list
+void ppStackFileActivationSingle(pmConfig *config, // Configuration
+                                 ppStackFileList list, // Files to turn on/off
+                                 bool state,   // Activation state
+                                 int num // Number of file in sequence
+                                 )
+{
+    assert(config);
+
+    char **files = stackFiles(list);    // Files to turn on/off
+    for (int i = 0; files[i] != NULL; i++) {
+        pmFPAfileActivateSingle(config->files, state, files[i], num); // Activated file
+    }
+    return;
+}
+
+// Iterate down the hierarchy, loading files; we can get away with this because we're working on skycells
+pmFPAview *ppStackFilesIterateDown(pmConfig *config // Configuration
+    )
+{
+    assert(config);
+
+    pmFPAview *view = pmFPAviewAlloc(0);// Pointer into FPA hierarchy
+    if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
+        return NULL;
+    }
+    view->chip = 0;
+    if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
+        return NULL;
+    }
+    view->cell = 0;
+    if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
+        return NULL;
+    }
+    view->readout = 0;
+    if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
+        return NULL;
+    }
+    return view;
+}
+
+// Iterate up the hierarchy, writing files; we can get away with this because we're working on skycells
+bool ppStackFilesIterateUp(pmConfig *config // Configuration
+                           )
+{
+    assert(config);
+
+    pmFPAview *view = pmFPAviewAlloc(0);// Pointer into FPA hierarchy
+    view->chip = view->cell = view->readout = 0;
+    if (!pmFPAfileIOChecks(config, view, PM_FPA_AFTER)) {
+        return false;
+    }
+    view->readout = -1;
+    if (!pmFPAfileIOChecks(config, view, PM_FPA_AFTER)) {
+        return false;
+    }
+    view->cell = -1;
+    if (!pmFPAfileIOChecks(config, view, PM_FPA_AFTER)) {
+        return false;
+    }
+    view->chip = -1;
+    if (!pmFPAfileIOChecks(config, view, PM_FPA_AFTER)) {
+        return false;
+    }
+    psFree(view);
+    return true;
+}
+
+// Write an image to a FITS file
+bool ppStackWriteImage(const char *name, // Name of image
+                       psMetadata *header, // Header
+                       const psImage *image, // Image
+                       pmConfig *config // Configuration
+    )
+{
+    assert(name);
+    assert(image);
+
+    psString resolved = pmConfigConvertFilename(name, config, true, true); // Resolved file name
+    psFits *fits = psFitsOpen(resolved, "w");
+    if (!fits) {
+        psError(PS_ERR_IO, false, "Unable to open FITS file %s to write image.", resolved);
+        psFree(resolved);
+        return false;
+    }
+    if (!psFitsWriteImage(fits, header, image, 0, NULL)) {
+        psError(PS_ERR_IO, false, "Unable to write FITS image %s.", resolved);
+        psFitsClose(fits);
+        psFree(resolved);
+        return false;
+    }
+    psFitsClose(fits);
+    psFree(resolved);
+    return true;
+}
Index: /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackFinish.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackFinish.c	(revision 23352)
+++ /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackFinish.c	(revision 23352)
@@ -0,0 +1,63 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <pslib.h>
+#include <psmodules.h>
+#include <ppStats.h>
+
+#include "ppStack.h"
+#include "ppStackLoop.h"
+
+bool ppStackFinish(ppStackOptions *options, pmConfig *config)
+{
+    psAssert(options, "Require options");
+    psAssert(config, "Require configuration");
+
+    psThreadPoolFinalize();
+
+    psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, PPSTACK_RECIPE); // ppStack recipe
+    psAssert(recipe, "We've thrown an error on this before.");
+
+    // Statistics on output
+    if (options->stats) {
+        psTrace("ppStack", 1, "Gathering statistics on stacked image....\n");
+        psString maskBadStr = psMetadataLookupStr(NULL, recipe, "MASK.BAD"); // Name of bits for bad
+        psImageMaskType maskBad = pmConfigMaskGet(maskBadStr, config); // Bits to mask for bad pixels
+
+        pmFPAview *view = pmFPAviewAlloc(0); // View to readout
+        view->chip = view->cell = view->readout = 0;
+
+        ppStatsFPA(options->stats, options->outRO->parent->parent->parent, view, maskBad, config);
+
+        psFree(view);
+    }
+
+    ppStackMemDump("stats");
+
+    psFree(options->outRO); options->outRO = NULL;
+
+    // Write out the output files
+    if (!ppStackFilesIterateUp(config)) {
+        return false;
+    }
+
+
+    // Write out summary statistics
+    if (options->stats) {
+        psMetadataAddF32(options->stats, PS_LIST_TAIL, "TIME_STACK", 0,
+                         "Total time", psTimerClear("PPSTACK_TOTAL"));
+
+        const char *statsMDC = psMetadataConfigFormat(options->stats);
+        if (!statsMDC || strlen(statsMDC) == 0) {
+            psError(PS_ERR_IO, false, "Unable to get statistics MDC file.\n");
+        } else {
+            fprintf(options->statsFile, "%s", statsMDC);
+        }
+        psFree((void *)statsMDC);
+        fclose(options->statsFile); options->statsFile = NULL;
+    }
+
+    return true;
+}
Index: /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackLoop.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackLoop.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackLoop.c	(revision 23352)
@@ -4,177 +4,9 @@
 
 #include <stdio.h>
-#include <unistd.h>
-#include <string.h>
-#include <libgen.h>
 #include <pslib.h>
 #include <psmodules.h>
-#include <ppStats.h>
 
 #include "ppStack.h"
-
-//#define TESTING
-
-#define WCS_TOLERANCE 0.001             // Tolerance for WCS
-#define PIXELS_BUFFER 1024              // Initial size of pixel lists
-
-// Here follows lists of files for activation/deactivation at various stages.  Each must be NULL-terminated.
-
-// Files required in preparation for convolution
-static char *prepareFiles[] = { "PPSTACK.INPUT.PSF", "PPSTACK.INPUT.SOURCES", "PPSTACK.TARGET.PSF", NULL };
-
-// Files required for the convolution
-static char *convolveFiles[] = { "PPSTACK.INPUT", "PPSTACK.INPUT.MASK", "PPSTACK.INPUT.VARIANCE", NULL };
-
-// Output files for the combination
-static char *combineFiles[] = { "PPSTACK.OUTPUT", "PPSTACK.OUTPUT.MASK", "PPSTACK.OUTPUT.VARIANCE",
-                                "PPSTACK.OUTPUT.JPEG1", "PPSTACK.OUTPUT.JPEG2", NULL };
-
-// Files for photometry
-static char *photFiles[] = { "PSPHOT.INPUT", "PSPHOT.OUTPUT", "PSPHOT.RESID", "PSPHOT.BACKMDL",
-                             "PSPHOT.BACKMDL.STDEV", "PSPHOT.BACKGND", "PSPHOT.BACKSUB",
-                             "SOURCE.PLOT.MOMENTS", "SOURCE.PLOT.PSFMODEL", "SOURCE.PLOT.APRESID",
-                             "PSPHOT.INPUT.CMF", NULL };
-
-static void memDump(const char *name)
-{
-    return;
-
-    static int num = 0;                 // Counter, to make files unique and give an idea of sequence
-
-    psString filename = NULL;           // Name of file
-    psStringAppend(&filename, "memdump_%s_%03d.txt", name, num);
-    FILE *memFile = fopen(filename, "w");
-    psFree(filename);
-
-    psMemBlock **leaks = NULL;
-    int numLeaks = psMemCheckLeaks(0, &leaks, NULL, true);
-    fprintf(memFile, "# MemBlock Size Source\n");
-    unsigned long total = 0;            // Total memory used
-    for (int i = 0; i < numLeaks; i++) {
-        psMemBlock *mb = leaks[i];
-        fprintf(memFile, "%12lu\t%12zd\t%s:%d\n", mb->id, mb->userMemorySize,
-                mb->file, mb->lineno);
-        total += mb->userMemorySize;
-    }
-    fclose(memFile);
-    psFree(leaks);
-
-    fprintf(stderr, "Memdump %s %d: Memory use: %ld, sbrk: %p\n", name, num, total, sbrk(0));
-    num++;
-}
-
-
-
-// Activate/deactivate a list of files
-static void fileActivation(pmConfig *config, // Configuration
-                           char **files, // Files to turn on/off
-                           bool state   // Activation state
-    )
-{
-    assert(config);
-    assert(files);
-
-    for (int i = 0; files[i] != NULL; i++) {
-        pmFPAfileActivate(config->files, state, files[i]);
-    }
-    return;
-}
-
-// Activate/deactivate a single element for a list
-static void fileActivationSingle(pmConfig *config, // Configuration
-                                 char **files, // Files to turn on/off
-                                 bool state,   // Activation state
-                                 int num // Number of file in sequence
-                                 )
-{
-    assert(config);
-    assert(files);
-
-    for (int i = 0; files[i] != NULL; i++) {
-        pmFPAfileActivateSingle(config->files, state, files[i], num); // Activated file
-    }
-    return;
-}
-
-// Iterate down the hierarchy, loading files; we can get away with this because we're working on skycells
-static pmFPAview *filesIterateDown(pmConfig *config // Configuration
-                                  )
-{
-    assert(config);
-
-    pmFPAview *view = pmFPAviewAlloc(0);// Pointer into FPA hierarchy
-    if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
-        return NULL;
-    }
-    view->chip = 0;
-    if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
-        return NULL;
-    }
-    view->cell = 0;
-    if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
-        return NULL;
-    }
-    view->readout = 0;
-    if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
-        return NULL;
-    }
-    return view;
-}
-
-// Iterate up the hierarchy, writing files; we can get away with this because we're working on skycells
-static bool filesIterateUp(pmConfig *config // Configuration
-                           )
-{
-    assert(config);
-
-    pmFPAview *view = pmFPAviewAlloc(0);// Pointer into FPA hierarchy
-    view->chip = view->cell = view->readout = 0;
-    if (!pmFPAfileIOChecks(config, view, PM_FPA_AFTER)) {
-        return false;
-    }
-    view->readout = -1;
-    if (!pmFPAfileIOChecks(config, view, PM_FPA_AFTER)) {
-        return false;
-    }
-    view->cell = -1;
-    if (!pmFPAfileIOChecks(config, view, PM_FPA_AFTER)) {
-        return false;
-    }
-    view->chip = -1;
-    if (!pmFPAfileIOChecks(config, view, PM_FPA_AFTER)) {
-        return false;
-    }
-    psFree(view);
-    return true;
-}
-
-// Write an image to a FITS file
-static bool writeImage(const char *name, // Name of image
-                       psMetadata *header, // Header
-                       const psImage *image, // Image
-                       pmConfig *config // Configuration
-                       )
-{
-    assert(name);
-    assert(image);
-
-    psString resolved = pmConfigConvertFilename(name, config, true, true); // Resolved file name
-    psFits *fits = psFitsOpen(resolved, "w");
-    if (!fits) {
-        psError(PS_ERR_IO, false, "Unable to open FITS file %s to write image.", resolved);
-        psFree(resolved);
-        return false;
-    }
-    if (!psFitsWriteImage(fits, header, image, 0, NULL)) {
-        psError(PS_ERR_IO, false, "Unable to write FITS image %s.", resolved);
-        psFitsClose(fits);
-        psFree(resolved);
-        return false;
-    }
-    psFitsClose(fits);
-    psFree(resolved);
-    return true;
-}
-
+#include "ppStackLoop.h"
 
 bool ppStackLoop(pmConfig *config)
@@ -183,1064 +15,126 @@
 
     psTimerStart("PPSTACK_TOTAL");
+    psTimerStart("PPSTACK_STEPS");
 
-    psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, PPSTACK_RECIPE); // ppStack recipe
-    psAssert(recipe, "We've thrown an error on this before.");
+    ppStackOptions *options = ppStackOptionsAlloc(); // Options for stacking
 
-    bool mdok;                          // Status of MD lookup
-    bool tempDelete = psMetadataLookupBool(&mdok, recipe, "TEMP.DELETE"); // Delete temporary files?
-    const char *tempDir = psMetadataLookupStr(NULL, recipe, "TEMP.DIR"); // Directory for temporary images
-    if (!tempDir) {
-        psError(PS_ERR_BAD_PARAMETER_VALUE, false, "Unable to find TEMP.DIR in recipe");
+    // Setup
+    psTrace("ppStack", 1, "Setup....\n");
+    if (!ppStackSetup(options, config)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to setup.");
+        psFree(options);
+        return false;
+    }
+    psLogMsg("ppStack", PS_LOG_INFO, "Stage 0: Setup: %f sec", psTimerClear("PPSTACK_STEPS"));
+    ppStackMemDump("setup");
+
+
+    // Preparation for stacking
+    psTrace("ppStack", 1, "Preparation for stacking: merging sources, determining target PSF....\n");
+    if (!ppStackPrepare(options, config)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to prepare for stacking.");
+        psFree(options);
+        return false;
+    }
+    psLogMsg("ppStack", PS_LOG_INFO, "Stage 1: Load Sources and Generate Target PSF: %f sec",
+             psTimerClear("PPSTACK_STEPS"));
+    ppStackMemDump("prepare");
+
+
+    // Convolve inputs
+    psTrace("ppStack", 1, "Convolving inputs to target PSF....\n");
+    if (!ppStackConvolve(options, config)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to convolve images.");
+        psFree(options);
+        return false;
+    }
+    psLogMsg("ppStack", PS_LOG_INFO, "Stage 2: Generate Convolutions and Save: %f sec",
+             psTimerClear("PPSTACK_STEPS"));
+    ppStackMemDump("convolve");
+
+
+    // Start threading
+    ppStackThreadInit();
+    ppStackThreadData *stack = ppStackThreadDataSetup(options, config);
+    if (!stack) {
+        psError(PS_ERR_IO, false, "Unable to initialise stack threads.");
+        psFree(options);
         return false;
     }
 
-    psString outputName = psStringCopy(psMetadataLookupStr(NULL, config->arguments, "OUTPUT")); // Name for temporary files
-    const char *tempName = basename(outputName);
-    if (!tempName) {
-        psError(PS_ERR_BAD_PARAMETER_VALUE, false, "Unable to construct basename for temporary files.");
+    // Initial combination
+    psTrace("ppStack", 1, "Initial stack of convolved images....\n");
+    if (!ppStackCombineInitial(stack, options, config)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to perform initial combination.");
+        psFree(stack);
+        psFree(options);
         return false;
     }
+    psLogMsg("ppStack", PS_LOG_INFO, "Stage 2: Generate Convolutions and Save: %f sec",
+             psTimerClear("PPSTACK_STEPS"));
+    ppStackMemDump("convolve");
+    psLogMsg("ppStack", PS_LOG_INFO, "Stage 4: Make Initial Stack: %f sec", psTimerClear("PPSTACK_STEPS"));
 
-    const char *tempImage = psMetadataLookupStr(NULL, recipe, "TEMP.IMAGE"); // Suffix for temporary images
-    const char *tempMask = psMetadataLookupStr(NULL, recipe, "TEMP.MASK"); // Suffix for temporary masks
-    const char *tempVariance = psMetadataLookupStr(NULL, recipe, "TEMP.VARIANCE"); // Suffix for temp variance maps
-    if (!tempImage || !tempMask || !tempVariance) {
-        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
-                "Unable to find TEMP.IMAGE, TEMP.MASK and TEMP.VARIANCE in recipe");
+
+    // Pixel rejection
+    psTrace("ppStack", 1, "Reject pixels....\n");
+    if (!ppStackReject(options, config)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to reject pixels.");
+        psFree(stack);
+        psFree(options);
         return false;
     }
+    psLogMsg("ppStack", PS_LOG_INFO, "Stage 5: Pixel Rejection: %f sec", psTimerClear("PPSTACK_STEPS"));
+    ppStackMemDump("reject");
 
-    float threshold = psMetadataLookupF32(NULL, recipe, "THRESHOLD.MASK"); // Threshold for mask deconvolution
-    float imageRej = psMetadataLookupF32(NULL, recipe, "IMAGE.REJ"); // Maximum fraction of image to reject
-                                                                     // before rejecting entire image
-    float poorFrac = psMetadataLookupF32(&mdok, recipe, "POOR.FRACTION"); // Fraction for "poor"
-
-    const char *statsName = psMetadataLookupStr(&mdok, config->arguments, "STATS"); // Filename for statistics
-    psMetadata *stats = NULL;           // Container for statistics
-    FILE *statsFile = NULL;             // File stream for statistics
-    if (statsName && strlen(statsName) > 0) {
-        psString resolved = pmConfigConvertFilename(statsName, config, true, true); // Resolved filename
-        statsFile = fopen(resolved, "w");
-        if (!statsFile) {
-            psError(PS_ERR_IO, true, "Unable to open statistics file %s for writing.\n", resolved);
-            psFree(resolved);
-            return false;
-        } else {
-            stats = psMetadataAlloc();
-        }
-        psFree(resolved);
-    }
-
-    pmFPAfile *output = psMetadataLookupPtr(NULL, config->files, "PPSTACK.OUTPUT"); // Output file
-    if (!output) {
-        psError(PS_ERR_UNEXPECTED_NULL, false, "Can't find output data!");
-        return false;
-    }
-    int num = psMetadataLookupS32(NULL, config->arguments, "INPUTS.NUM"); // Number of inputs
-
-    psMetadata *ppsub = psMetadataLookupMetadata(NULL, config->recipes, "PPSUB"); // PPSUB recipe
-    int overlap = 2 * psMetadataLookupS32(NULL, ppsub,
-                                          "KERNEL.SIZE"); // Overlap by kernel size between consecutive scans
-
-    if (!pmConfigMaskSetBits(NULL, NULL, config)) {
-        psError(PS_ERR_UNKNOWN, false, "Unable to determine mask value.");
-        return false;
-    }
-
-    memDump("start");
-
-    psLogMsg("ppStack", PS_LOG_INFO, "Stage 0 : Initialization and Configuration : %f sec", psTimerClear("PPSTACK_STEPS"));
-
-    // Preparation iteration: Load the sources, and get a target PSF model
-    psTrace("ppStack", 1, "Determining target PSF....\n");
-    psArray *sourceLists = psArrayAlloc(num); // Individual lists of sources for matching
-    pmPSF *targetPSF = NULL;            // Target PSF
-    float sumExposure = NAN;            // Sum of exposure times
-    if (psMetadataLookupBool(NULL, config->arguments, "HAVE.PSF")) {
-        pmFPAfileActivate(config->files, false, NULL);
-        fileActivation(config, prepareFiles, true);
-        pmFPAview *view = filesIterateDown(config);
-        if (!view) {
-            return false;
-        }
-
-        // Generate list of PSFs
-        psMetadataIterator *fileIter = psMetadataIteratorAlloc(config->files, PS_LIST_HEAD,
-                                                               "^PPSTACK.INPUT$"); // Iterator
-        psMetadataItem *fileItem; // Item from iteration
-        psArray *psfs = psArrayAlloc(num); // PSFs for PSF envelope
-        int numCols = 0, numRows = 0;   // Size of image
-        int index = 0;                  // Index for file
-        while ((fileItem = psMetadataGetAndIncrement(fileIter))) {
-            assert(fileItem->type == PS_DATA_UNKNOWN);
-            pmFPAfile *inputFile = fileItem->data.V; // An input file
-            pmChip *chip = pmFPAviewThisChip(view, inputFile->fpa); // The chip: holds the PSF
-            pmPSF *psf = psMetadataLookupPtr(NULL, chip->analysis, "PSPHOT.PSF"); // PSF
-            if (!psf) {
-                psError(PS_ERR_UNKNOWN, false, "Unable to find PSF.");
-                psFree(view);
-                psFree(sourceLists);
-                psFree(fileIter);
-                psFree(psfs);
-                return false;
-            }
-            psfs->data[index] = psMemIncrRefCounter(psf);
-            psMetadataRemoveKey(chip->analysis, "PSPHOT.PSF");
-
-            pmCell *cell = pmFPAviewThisCell(view, inputFile->fpa); // Cell of interest
-            pmHDU *hdu = pmHDUFromCell(cell);
-            assert(hdu && hdu->header);
-            int naxis1 = psMetadataLookupS32(NULL, hdu->header, "NAXIS1"); // Number of columns
-            int naxis2 = psMetadataLookupS32(NULL, hdu->header, "NAXIS2"); // Number of rows
-            if (naxis1 <= 0 || naxis2 <= 0) {
-                psError(PS_ERR_UNKNOWN, false, "Unable to determine size of image from PSF.");
-                psFree(view);
-                psFree(sourceLists);
-                psFree(fileIter);
-                psFree(psfs);
-                return false;
-            }
-            if (numCols == 0 && numRows == 0) {
-                numCols = naxis1;
-                numRows = naxis2;
-            }
-
-            pmReadout *ro = pmFPAviewThisReadout(view, inputFile->fpa); // Readout with sources
-            psArray *sources = psMetadataLookupPtr(NULL, ro->analysis, "PSPHOT.SOURCES"); // Sources
-            if (!sources) {
-                psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to find sources in readout.");
-                return NULL;
-            }
-            sourceLists->data[index] = psMemIncrRefCounter(sources);
-
-            // Re-do photometry if we don't trust the source lists
-            if (psMetadataLookupBool(NULL, recipe, "PHOT")) {
-                psTrace("ppStack", 2, "Photometering input %d of %d....\n", index, num);
-                pmFPAfileActivate(config->files, false, NULL);
-                fileActivationSingle(config, convolveFiles, true, index);
-                pmFPAfile *file = pmFPAfileSelectSingle(config->files, "PPSTACK.INPUT", index); // File
-                pmFPAview *view = filesIterateDown(config);
-                if (!view) {
-                    psFree(sourceLists);
-                    psFree(targetPSF);
-                    return false;
-                }
-
-                pmReadout *ro = pmFPAviewThisReadout(view, file->fpa); // Readout of interest
-
-                if (!ppStackInputPhotometry(ro, sources, config)) {
-                    psError(PS_ERR_UNKNOWN, false, "Unable to do photometry on input sources");
-                    psFree(sourceLists);
-                    psFree(targetPSF);
-                    return false;
-                }
-
-                psFree(view);
-                if (!filesIterateUp(config)) {
-                    psFree(sourceLists);
-                    psFree(targetPSF);
-                    return false;
-                }
-                pmFPAfileActivate(config->files, false, NULL);
-                fileActivation(config, prepareFiles, true);
-            }
-
-            index++;
-        }
-        psFree(fileIter);
-
-        // Zero point calibration
-        sumExposure = ppStackSourcesTransparency(sourceLists, view, config);
-        if (!isfinite(sumExposure) || sumExposure <= 0) {
-            psError(PS_ERR_UNKNOWN, false, "Unable to calculate transparency differences");
-            psFree(sourceLists);
-            psFree(targetPSF);
-            return false;
-        }
-
-        // Generate target PSF
-        targetPSF = ppStackPSF(config, numCols, numRows, psfs);
-        psFree(psfs);
-        if (!targetPSF) {
-            psError(PS_ERR_UNKNOWN, false, "Unable to determine output PSF.");
-            psFree(sourceLists);
-            psFree(view);
-            return false;
-        }
-        psMetadataAddPtr(config->arguments, PS_LIST_TAIL, "PSF.TARGET", PS_DATA_UNKNOWN,
-                         "Target PSF for stack", targetPSF);
-
-        pmChip *outChip = pmFPAviewThisChip(view, output->fpa); // Output chip
-        psMetadataAddPtr(outChip->analysis, PS_LIST_TAIL, "PSPHOT.PSF", PS_DATA_UNKNOWN,
-                         "Target PSF", targetPSF);
-        outChip->data_exists = true;
-
-        psFree(view);
-        if (!filesIterateUp(config)) {
-            return false;
-        }
-    }
-
-    psLogMsg("ppStack", PS_LOG_INFO, "Stage 1 : Load Sources and Generate Target PSF : %f sec", psTimerClear("PPSTACK_STEPS"));
-    memDump("psf");
-
-    psArray *imageNames = psArrayAlloc(num);
-    psArray *maskNames = psArrayAlloc(num);
-    psArray *varianceNames = psArrayAlloc(num);
-    for (int i = 0; i < num; i++) {
-        psString imageName = NULL, maskName = NULL, varianceName = NULL; // Names for convolved images
-        psStringAppend(&imageName, "%s/%s.%d.%s", tempDir, tempName, i, tempImage);
-        psStringAppend(&maskName, "%s/%s.%d.%s", tempDir, tempName, i, tempMask);
-        psStringAppend(&varianceName, "%s/%s.%d.%s", tempDir, tempName, i, tempVariance);
-        psTrace("ppStack", 5, "Temporary files: %s %s %s\n", imageName, maskName, varianceName);
-        imageNames->data[i] = imageName;
-        maskNames->data[i] = maskName;
-        varianceNames->data[i] = varianceName;
-    }
-    // Free the outputName that we grabbed above to set up tempName.
-    psFree(outputName);
-
-    // Generate convolutions and write them to disk
-    psTrace("ppStack", 1, "Convolving inputs to target PSF....\n");
-    psArray *cells = psArrayAlloc(num); // Cells for convolved images --- a handle for reading again
-    psArray *subKernels = psArrayAlloc(num); // Subtraction kernels --- required in the stacking
-    psArray *subRegions = psArrayAlloc(num); // Subtraction regions --- required in the stacking
-    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 0); // Random number generator
-    int numGood = 0;                    // Number of good frames
-    int numCols = 0, numRows = 0;       // Size of image
-    psVector *inputMask = psVectorAlloc(num, PS_TYPE_VECTOR_MASK); // Mask for inputs
-    psVectorInit(inputMask, 0);
-    psVector *matchChi2 = psVectorAlloc(num, PS_TYPE_F32); // chi^2 for stamps when matching
-    psVectorInit(matchChi2, NAN);
-    psVector *weightings = psVectorAlloc(num, PS_TYPE_F32); // Combination weightings for images (1/noise^2)
-    psVectorInit(weightings, NAN);
-    psList *fpaList = psListAlloc(NULL); // List of input FPAs, for concept averaging
-    psList *cellList = psListAlloc(NULL); // List of input cells, for concept averaging
-    psArray *covariances = psArrayAlloc(num); // Covariance matrices
-    for (int i = 0; i < num; i++) {
-        psTrace("ppStack", 2, "Convolving input %d of %d to target PSF....\n", i, num);
-        pmFPAfileActivate(config->files, false, NULL);
-        fileActivationSingle(config, convolveFiles, true, i);
-        pmFPAfile *file = pmFPAfileSelectSingle(config->files, "PPSTACK.INPUT", i); // File of interest
-        pmFPAview *view = filesIterateDown(config);
-        if (!view) {
-            psFree(sourceLists);
-            psFree(targetPSF);
-            psFree(rng);
-            psFree(inputMask);
-            psFree(matchChi2);
-            psFree(fpaList);
-            psFree(cellList);
-            psFree(covariances);
-            return false;
-        }
-
-        pmReadout *readout = pmFPAviewThisReadout(view, file->fpa); // Input readout
-        psFree(view);
-
-        if (numCols == 0 && numRows == 0) {
-            numCols = readout->image->numCols;
-            numRows = readout->image->numRows;
-        } else if (numCols != readout->image->numCols || numRows != readout->image->numRows) {
-            psError(PS_ERR_UNKNOWN, true, "Sizes of input images don't match: %dx%d vs %dx%d",
-                    readout->image->numCols, readout->image->numRows, numCols, numRows);
-            psFree(sourceLists);
-            psFree(targetPSF);
-            psFree(rng);
-            psFree(inputMask);
-            psFree(matchChi2);
-            psFree(fpaList);
-            psFree(cellList);
-            psFree(covariances);
-            return false;
-        }
-
-        // Background subtraction, scaling and normalisation is performed automatically by the image matching
-        psArray *regions = NULL, *kernels = NULL; // Regions and kernels used in subtraction
-        psTimerStart("PPSTACK_MATCH");
-
-        if (!ppStackMatch(readout, &regions, &kernels, &matchChi2->data.F32[i], &weightings->data.F32[i],
-                          sourceLists->data[i], targetPSF, rng, config)) {
-            psErrorStackPrint(stderr, "Unable to match image %d --- ignoring.", i);
-            inputMask->data.PS_TYPE_VECTOR_MASK_DATA[i] = PPSTACK_MASK_MATCH;
-            psErrorClear();
-            continue;
-        }
-        covariances->data[i] = psMemIncrRefCounter(readout->covariance);
-
-        if (stats) {
-            pmSubtractionKernels *kernels = psMetadataLookupPtr(NULL, readout->analysis,
-                                                                PM_SUBTRACTION_ANALYSIS_KERNEL);// Conv kernel
-            psMetadataAddF32(stats, PS_LIST_TAIL, "TIME_MATCH", PS_META_DUPLICATE_OK,
-                             "Time to match PSF", psTimerMark("PPSTACK_MATCH"));
-            psMetadataAddF32(stats, PS_LIST_TAIL, "STAMP.MEAN", PS_META_DUPLICATE_OK,
-                             "Mean deviation for stamps", kernels->mean);
-            psMetadataAddF32(stats, PS_LIST_TAIL, "STAMP.RMS", PS_META_DUPLICATE_OK,
-                             "RMS deviation for stamps", kernels->rms);
-            psMetadataAddF32(stats, PS_LIST_TAIL, "STAMP.NUM", PS_META_DUPLICATE_OK,
-                             "Number of stamps", kernels->numStamps);
-            float deconv = psMetadataLookupF32(NULL, readout->analysis,
-                                               PM_SUBTRACTION_ANALYSIS_DECONV_MAX); // Deconvolution fraction
-            psMetadataAddF32(stats, PS_LIST_TAIL, "KERNEL.DECONV", PS_META_DUPLICATE_OK,
-                             "Deconvolution fraction for kernel", deconv);
-            psMetadataAddF32(stats, PS_LIST_TAIL, "PPSTACK.WEIGHTING", PS_META_DUPLICATE_OK,
-                             "Weighting for image", weightings->data.F32[i]);
-        }
-        psLogMsg("ppStack", PS_LOG_INFO, "Time to match image %d: %f sec", i, psTimerClear("PPSTACK_MATCH"));
-
-        subRegions->data[i] = regions;
-        subKernels->data[i] = kernels;
-
-        // Write the temporary convolved files
-        pmHDU *hdu = readout->parent->parent->parent->hdu; // HDU for convolved image
-        assert(hdu);
-        writeImage(imageNames->data[i], hdu->header, readout->image, config);
-        psMetadata *maskHeader = psMetadataCopy(NULL, hdu->header); // Copy of header, for mask
-        pmConfigMaskWriteHeader(config, maskHeader);
-        writeImage(maskNames->data[i], maskHeader, readout->mask, config);
-        psFree(maskHeader);
-        psImageCovarianceTransfer(readout->variance, readout->covariance);
-        writeImage(varianceNames->data[i], hdu->header, readout->variance, config);
-#ifdef TESTING
-        {
-            psString name = NULL;
-            psStringAppend(&name, "covariance_%d.fits", i);
-            writeImage(name, hdu->header, readout->covariance->image, config);
-            psFree(name);
-        }
-#endif
-
-        pmCell *inCell = readout->parent; // Input cell
-
-        psListAdd(cellList, PS_LIST_TAIL, inCell);
-        psListAdd(fpaList, PS_LIST_TAIL, inCell->parent->parent);
-
-        cells->data[i] = psMemIncrRefCounter(inCell);
-        if (!filesIterateUp(config)) {
-            psFree(fpaList);
-            psFree(cellList);
-            psFree(covariances);
-            return false;
-        }
-        numGood++;
-
-        memDump("match");
-    }
-    psFree(sourceLists);
-    psFree(targetPSF);
-    psFree(rng);
-
-    psLogMsg("ppStack", PS_LOG_INFO, "Stage 2 : Generate Convolutions and Save : %f sec", psTimerClear("PPSTACK_STEPS"));
-
-    if (numGood == 0) {
-        psError(PS_ERR_UNKNOWN, false, "No good images to combine.");
-        psFree(subKernels);
-        psFree(subRegions);
-        psFree(cells);
-        psFree(inputMask);
-        psFree(matchChi2);
-        psFree(fpaList);
-        psFree(cellList);
-        psFree(covariances);
-        return false;
-    }
-
-
-    // Update concepts for output
-    {
-        pmFPAview view;                 // View for output
-        view.chip = view.cell = view.readout = 0;
-        pmCell *outCell = pmFPAfileThisCell(config->files, &view, "PPSTACK.OUTPUT"); // Output cell
-        pmFPA *outFPA = outCell->parent->parent; // Output FPA
-        pmConceptsAverageFPAs(outFPA, fpaList);
-        pmConceptsAverageCells(outCell, cellList, NULL, NULL, true);
-        psFree(fpaList);
-        psFree(cellList);
-
-        // Update the value of a concept
-#define UPDATE_CONCEPT(SOURCE, NAME, VALUE) { \
-            psMetadataItem *item = psMetadataLookup(SOURCE->concepts, NAME); \
-            psAssert(item, "Concept should be present"); \
-            psAssert(item->type == PS_TYPE_F32, "Concept should be F32"); \
-            item->data.F32 = VALUE; \
-        }
-
-        UPDATE_CONCEPT(outFPA,  "FPA.EXPOSURE",  sumExposure);
-        UPDATE_CONCEPT(outCell, "CELL.EXPOSURE", sumExposure);
-        UPDATE_CONCEPT(outCell, "CELL.DARKTIME", NAN);
-    }
-
-
-    // Reject images out-of-hand on the basis of their match chi^2
-    {
-        psVector *values = psVectorAllocEmpty(num, PS_TYPE_F32); // Values to sort
-        for (int i = 0; i < num; i++) {
-            if (inputMask->data.PS_TYPE_VECTOR_MASK_DATA[i] & PPSTACK_MASK_ALL) {
-                continue;
-            }
-            values->data.F32[values->n++] = matchChi2->data.F32[i];
-        }
-        assert(values->n == numGood);
-        if (!psVectorSortInPlace(values)) {
-            psError(PS_ERR_UNKNOWN, false, "Unable to sort vector.");
-            psFree(subKernels);
-            psFree(subRegions);
-            psFree(cells);
-            psFree(inputMask);
-            psFree(matchChi2);
-            psFree(values);
-            psFree(covariances);
-            return false;
-        }
-        float median = numGood % 2 ? values->data.F32[numGood / 2] :
-            0.5 * (values->data.F32[numGood / 2 - 1] + values->data.F32[numGood / 2]);
-
-        float rms = 0.74 * (values->data.F32[numGood * 3 / 4] -
-                            values->data.F32[numGood / 4]); // Estimated RMS from interquartile range
-
-        psFree(values);
-
-        float rej = psMetadataLookupF32(NULL, recipe, "MATCH.REJ"); // Rejection threshold (stdevs)
-        if (isfinite(rej)) {
-            float thresh = median + rej * rms; // Threshold for rejection
-            psLogMsg("ppStack", PS_LOG_INFO, "chi^2 rejection threshold = %f + %f * %f = %f",
-                     median, rej, rms, thresh);
-
-            int numRej = 0;                 // Number rejected
-            numGood = 0;                    // Number of good images
-            for (int i = 0; i < num; i++) {
-              if (inputMask->data.PS_TYPE_VECTOR_MASK_DATA[i] & PPSTACK_MASK_ALL) {
-                    continue;
-                }
-                if (matchChi2->data.F32[i] > thresh) {
-                    numRej++;
-                    inputMask->data.PS_TYPE_VECTOR_MASK_DATA[i] |= PPSTACK_MASK_CHI2;
-                    psLogMsg("ppStack", PS_LOG_INFO, "Rejecting image %d because of large matching chi^2: %f",
-                             i, matchChi2->data.F32[i]);
-                } else {
-                    psLogMsg("ppStack", PS_LOG_INFO, "Image %d has matching chi^2: %f",
-                             i, matchChi2->data.F32[i]);
-                    numGood++;
-                }
-            }
-        }
-    }
-
-    if (numGood == 0) {
-        psError(PS_ERR_UNKNOWN, false, "No good images to combine.");
-        psFree(subKernels);
-        psFree(subRegions);
-        psFree(cells);
-        psFree(inputMask);
-        psFree(matchChi2);
-        psFree(covariances);
-        return false;
-    }
-
-#ifdef TESTING
-    psTraceSetLevel("psModules.imcombine", 7);
-#endif
-
-    psLogMsg("ppStack", PS_LOG_INFO, "Stage 3 : Basic Image Rejection  : %f sec", psTimerClear("PPSTACK_STEPS"));
-
-    // Start threading
-    ppStackThreadInit();
-    ppStackThreadData *stack = ppStackThreadDataSetup(cells, imageNames, maskNames, varianceNames,
-                                                      covariances, config);
-    if (!stack) {
-        psError(PS_ERR_IO, false, "Unable to initialise stack threads.");
-        psFree(subKernels);
-        psFree(subRegions);
-        psFree(inputMask);
-        psFree(matchChi2);
-        psFree(cells);
-        psFree(covariances);
-        return false;
-    }
-
-    psTimerStart("PPSTACK_INITIAL");
-
-    memDump("preinitial");
-
-    // Stack the convolved files
-    psTrace("ppStack", 1, "Initial stack of convolved images....\n");
-    pmReadout *outRO = NULL;            // Output readout
-    pmFPAview *view = NULL;             // View to readout
-    psArray *inspect = NULL;            // Array of arrays of pixels to inspect
-    {
-        int row0, col0;                 // Offset for readout
-        int numCols, numRows;           // Size of readout
-        ppStackThread *thread = stack->threads->data[0]; // Representative thread
-        if (!pmReadoutStackSetOutputSize(&col0, &row0, &numCols, &numRows, thread->readouts)) {
-            psError(PS_ERR_UNKNOWN, false, "problem setting output readout size.");
-            psFree(subKernels);
-            psFree(subRegions);
-            psFree(stack);
-            psFree(inputMask);
-            psFree(matchChi2);
-            psFree(cells);
-            psFree(covariances);
-            return false;
-        }
-
-        pmFPAfileActivate(config->files, false, NULL);
-        fileActivation(config, combineFiles, true);
-        view = filesIterateDown(config);
-        if (!view) {
-            psFree(subKernels);
-            psFree(subRegions);
-            psFree(stack);
-            psFree(inputMask);
-            psFree(matchChi2);
-            psFree(cells);
-            psFree(covariances);
-            return false;
-        }
-
-        pmCell *outCell = pmFPAfileThisCell(config->files, view, "PPSTACK.OUTPUT"); // Output cell
-        outRO = pmReadoutAlloc(outCell); // Output readout
-
-        psString maskBadStr = psMetadataLookupStr(NULL, recipe, "MASK.BAD"); // Name of bits to mask for bad
-        psImageMaskType maskBad = pmConfigMaskGet(maskBadStr, config); // Bits to mask for bad pixels
-        if (!pmReadoutStackDefineOutput(outRO, col0, row0, numCols, numRows, true, true, maskBad)) {
-            psError(PS_ERR_UNKNOWN, false, "Unable to prepare output.");
-            psFree(subKernels);
-            psFree(subRegions);
-            psFree(stack);
-            psFree(inputMask);
-            psFree(matchChi2);
-            psFree(view);
-            psFree(outRO);
-            psFree(cells);
-            psFree(covariances);
-            return false;
-        }
-
-        psFree(cells);
-
-        bool status;                    // Status of read
-        int numChunk;                   // Number of chunks
-        for (numChunk = 0; true; numChunk++) {
-            ppStackThread *thread = ppStackThreadRead(&status, stack, config, numChunk, overlap);
-            if (!status) {
-                // Something went wrong
-                psError(PS_ERR_UNKNOWN, false, "Unable to read chunk %d", numChunk);
-                psFree(subKernels);
-                psFree(subRegions);
-                psFree(stack);
-                psFree(inputMask);
-                psFree(matchChi2);
-                psFree(view);
-                psFree(outRO);
-                psFree(covariances);
-                return false;
-            }
-            if (!thread) {
-                // Nothing more to read
-                break;
-            }
-
-            // call: ppStackReadoutInitial(config, outRO, readouts, subRegions, subKernels)
-            psThreadJob *job = psThreadJobAlloc("PPSTACK_INITIAL_COMBINE"); // Job to start
-            psArrayAdd(job->args, 1, thread);
-            psArrayAdd(job->args, 1, config);
-            psArrayAdd(job->args, 1, outRO);
-            psArrayAdd(job->args, 1, inputMask);
-            psArrayAdd(job->args, 1, weightings);
-            psArrayAdd(job->args, 1, matchChi2);
-            if (!psThreadJobAddPending(job)) {
-                psFree(job);
-                psFree(subKernels);
-                psFree(subRegions);
-                psFree(stack);
-                psFree(inputMask);
-                psFree(weightings);
-                psFree(matchChi2);
-                psFree(view);
-                psFree(outRO);
-                psFree(covariances);
-                return false;
-            }
-            psFree(job);
-        }
-
-        if (!psThreadPoolWait(false)) {
-            psError(PS_ERR_UNKNOWN, false, "Unable to do initial combination.");
-            psFree(subKernels);
-            psFree(subRegions);
-            psFree(stack);
-            psFree(inputMask);
-            psFree(matchChi2);
-            psFree(view);
-            psFree(outRO);
-            psFree(covariances);
-            return false;
-        }
-
-        // Harvest the jobs, gathering the inspection lists
-        inspect = psArrayAlloc(num);
-        for (int i = 0; i < num; i++) {
-            if (inputMask->data.PS_TYPE_VECTOR_MASK_DATA[i]) {
-                continue;
-            }
-            inspect->data[i] = psArrayAllocEmpty(numChunk);
-        }
-        psThreadJob *job;               // Completed job
-        while ((job = psThreadJobGetDone())) {
-            psAssert(strcmp(job->type, "PPSTACK_INITIAL_COMBINE") == 0,
-                     "Job has incorrect type: %s", job->type);
-            psArray *results = job->results; // Results of job
-            for (int i = 0; i < num; i++) {
-                if (inputMask->data.PS_TYPE_VECTOR_MASK_DATA[i]) {
-                    continue;
-                }
-                inspect->data[i] = psArrayAdd(inspect->data[i], 1, results->data[i]);
-            }
-            psFree(job);
-        }
-
-        memDump("initial");
-    }
-
-#ifdef TESTING
-    writeImage("combined_initial.fits", NULL, outRO->image, config);
-#endif
-
-    if (stats) {
-        psMetadataAddF32(stats, PS_LIST_TAIL, "TIME_INITIAL", 0,
-                         "Time to make initial stack", psTimerMark("PPSTACK_INITIAL"));
-    }
-    psLogMsg("ppStack", PS_LOG_INFO, "Stage 4 : Make Initial Stack : %f sec", psTimerClear("PPSTACK_STEPS"));
-    psLogMsg("ppStack", PS_LOG_INFO, "Time to make initial stack: %f sec", psTimerClear("PPSTACK_INITIAL"));
-
-    psTimerStart("PPSTACK_REJECT");
-
-    // Pixel rejection
-    psArray *rejected = psArrayAlloc(num);
-    {
-        int numRejected = 0;        // Number of inputs rejected completely
-
-        // Count images rejected out of hand
-        for (int i = 0; i < num; i++) {
-            if (inputMask->data.PS_TYPE_VECTOR_MASK_DATA[i]) {
-                numRejected++;
-            }
-        }
-
-        for (int i = 0; i < num; i++) {
-            if (inputMask->data.PS_TYPE_VECTOR_MASK_DATA[i]) {
-                continue;
-            }
-
-            psThreadJob *job = psThreadJobAlloc("PPSTACK_INSPECT"); // Job to start
-            psArrayAdd(job->args, 1, inspect);
-            PS_ARRAY_ADD_SCALAR(job->args, i, PS_TYPE_S32);
-            if (!psThreadJobAddPending(job)) {
-                psFree(job);
-                psFree(subKernels);
-                psFree(subRegions);
-                psFree(stack);
-                psFree(inputMask);
-                psFree(view);
-                psFree(outRO);
-                psFree(inspect);
-                psFree(rejected);
-                psFree(covariances);
-                psFree(matchChi2);
-                return false;
-            }
-            psFree(job);
-        }
-
-        if (!psThreadPoolWait(true)) {
-            psError(PS_ERR_UNKNOWN, false, "Unable to concatenate inspection lists.");
-            psFree(subKernels);
-            psFree(subRegions);
-            psFree(stack);
-            psFree(inputMask);
-            psFree(view);
-            psFree(outRO);
-            psFree(inspect);
-            psFree(rejected);
-            psFree(covariances);
-            psFree(matchChi2);
-            return false;
-        }
-
-        if (psMetadataLookupS32(NULL, config->arguments, "-threads") > 0) {
-            pmStackRejectThreadsInit();
-        }
-
-        int stride = psMetadataLookupS32(NULL, ppsub, "STRIDE"); // Size of convolution patches
-
-        // Reject bad pixels
-        for (int i = 0; i < num; i++) {
-            if (inputMask->data.PS_TYPE_VECTOR_MASK_DATA[i]) {
-                continue;
-            }
-            psTimerStart("PPSTACK_REJECT");
-
-#ifdef TESTING
-            {
-                psImage *mask = psPixelsToMask(NULL, inspect->data[i],
-                                               psRegionSet(0, numCols - 1, 0, numRows - 1),
-                                               0xff); // Mask image
-                psString name = NULL;           // Name of image
-                psStringAppend(&name, "inspect_%03d.fits", i);
-                psFits *fits = psFitsOpen(name, "w");
-                psFree(name);
-                psFitsWriteImage(fits, NULL, mask, 0, NULL);
-                psFree(mask);
-                psFitsClose(fits);
-            }
-#endif
-
-            psPixels *reject = pmStackReject(inspect->data[i], numCols, numRows, threshold, poorFrac, stride,
-                                             subRegions->data[i], subKernels->data[i]); // Rejected pixels
-
-#ifdef TESTING
-            {
-                psImage *mask = psPixelsToMask(NULL, reject, psRegionSet(0, numCols - 1, 0, numRows - 1),
-                                               0xff); // Mask image
-                psString name = NULL;           // Name of image
-                psStringAppend(&name, "reject_%03d.fits", i);
-                psFits *fits = psFitsOpen(name, "w");
-                psFree(name);
-                psFitsWriteImage(fits, NULL, mask, 0, NULL);
-                psFree(mask);
-                psFitsClose(fits);
-            }
-#endif
-
-            psFree(inspect->data[i]);
-            inspect->data[i] = NULL;
-
-            if (!reject) {
-                psWarning("Rejection on image %d didn't work --- reject entire image.", i);
-                numRejected++;
-                inputMask->data.PS_TYPE_VECTOR_MASK_DATA[i] = PPSTACK_MASK_REJECT;
-            } else {
-                float frac = reject->n / (float)(numCols * numRows); // Pixel fraction
-                psLogMsg("ppStack", PS_LOG_INFO, "%ld pixels rejected from image %d (%.1f%%)",
-                         reject->n, i, frac * 100.0);
-                if (frac > imageRej) {
-                    psWarning("Image %d rejected completely because rejection fraction (%.3f) "
-                              "exceeds limit (%.3f)", i, frac, imageRej);
-                    psFree(reject);
-                    // reject == NULL means reject image completely
-                    reject = NULL;
-                    inputMask->data.PS_TYPE_VECTOR_MASK_DATA[i] = PPSTACK_MASK_BAD;
-                    numRejected++;
-                }
-            }
-
-            // Images without a list of rejected pixels (the list may be empty) are rejected completely
-            rejected->data[i] = reject;
-
-            if (stats) {
-                psMetadataAddF32(stats, PS_LIST_TAIL, "TIME_REJECT", PS_META_DUPLICATE_OK,
-                                 "Time to perform rejection", psTimerMark("PPSTACK_REJECT"));
-                psMetadataAddS32(stats, PS_LIST_TAIL, "REJECT_PIXELS", PS_META_DUPLICATE_OK,
-                                 "Number of pixels rejected", reject ? reject->n : 0);
-            }
-            psLogMsg("ppStack", PS_LOG_INFO, "Time to perform rejection on image %d: %f sec", i,
-                     psTimerClear("PPSTACK_REJECT"));
-        }
-
-        psFree(inspect);
-        psFree(subKernels);
-        psFree(subRegions);
-
-        if (numRejected >= num - 1) {
-            psError(PS_ERR_UNKNOWN, true, "All inputs completely rejected; unable to proceed.");
-            psFree(stack);
-            psFree(rejected);
-            psFree(inputMask);
-            psFree(view);
-            psFree(outRO);
-            psFree(covariances);
-            psFree(matchChi2);
-            return false;
-        }
-
-        if (stats) {
-            psMetadataAddS32(stats, PS_LIST_TAIL, "REJECT_IMAGES", 0,
-                             "Number of images rejected completely", numRejected);
-        }
-    }
-
-    psLogMsg("ppStack", PS_LOG_INFO, "Stage 5 : Pixel Rejection : %f sec", psTimerClear("PPSTACK_STEPS"));
-    psTimerStart("PPSTACK_FINAL");
-
-    memDump("reject");
 
     // Final combination
     psTrace("ppStack", 2, "Final stack of convolved images....\n");
-    {
-        stack->lastScan = 0;            // Reset read
-        bool status;                    // Status of read
-        for (int numChunk = 0; true; numChunk++) {
-            ppStackThread *thread = ppStackThreadRead(&status, stack, config, numChunk, 0);
-            if (!status) {
-                // Something went wrong
-                psError(PS_ERR_UNKNOWN, false, "Unable to read chunk %d", numChunk);
-                psFree(stack);
-                psFree(rejected);
-                psFree(inputMask);
-                psFree(view);
-                psFree(outRO);
-                psFree(covariances);
-                psFree(matchChi2);
-                return false;
-            }
-            if (!thread) {
-                // Nothing more to read
-                break;
-            }
+    if (!ppStackCombineFinal(stack, options, config)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to perform final combination.");
+        psFree(stack);
+        psFree(options);
+        return false;
+    }
+    psLogMsg("ppStack", PS_LOG_INFO, "Stage 6: Final Stack: %f sec", psTimerClear("PPSTACK_STEPS"));
+    ppStackMemDump("final");
 
-            // call: ppStackReadoutFinal(config, outRO, readouts, rejected)
-            psThreadJob *job = psThreadJobAlloc("PPSTACK_FINAL_COMBINE"); // Job to start
-            psArrayAdd(job->args, 1, thread);
-            psArrayAdd(job->args, 1, config);
-            psArrayAdd(job->args, 1, outRO);
-            psArrayAdd(job->args, 1, inputMask);
-            psArrayAdd(job->args, 1, rejected);
-            psArrayAdd(job->args, 1, weightings);
-            psArrayAdd(job->args, 1, matchChi2);
-            if (!psThreadJobAddPending(job)) {
-                psFree(job);
-                psFree(stack);
-                psFree(rejected);
-                psFree(inputMask);
-                psFree(view);
-                psFree(outRO);
-                psFree(covariances);
-                psFree(matchChi2);
-                return false;
-            }
-            psFree(job);
-        }
 
-        if (!psThreadPoolWait(true)) {
-            psError(PS_ERR_UNKNOWN, false, "Unable to do final combination.");
-            psFree(stack);
-            psFree(inputMask);
-            psFree(rejected);
-            psFree(view);
-            psFree(outRO);
-            psFree(covariances);
-            psFree(matchChi2);
-            return false;
-        }
+    // Clean up
+    psTrace("ppStack", 2, "Cleaning up after combination....\n");
+    if (!ppStackCombineFinal(stack, options, config)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to clean up.");
+        psFree(stack);
+        psFree(options);
+        return false;
     }
+    psLogMsg("ppStack", PS_LOG_INFO, "Stage 7: WCS & JPEGS: %f sec", psTimerClear("PPSTACK_STEPS"));
+    ppStackMemDump("cleanup");
 
-    memDump("final");
-
-#ifdef TESTING
-    writeImage("combined_final.fits", NULL, outRO->image, config);
-#endif
-
-    // Sum covariance matrices
-    for (int i = 0; i < num; i++) {
-        if (inputMask->data.U8[i]) {
-            psFree(covariances->data[i]);
-            covariances->data[i] = NULL;
-        }
-    }
-    outRO->covariance = psImageCovarianceSum(covariances);
-    psFree(covariances);
-    psFree(matchChi2);
-
-    if (stats) {
-        psMetadataAddF32(stats, PS_LIST_TAIL, "TIME_FINAL", 0,
-                         "Time to make final stack", psTimerMark("PPSTACK_FINAL"));
-    }
-
-    psLogMsg("ppStack", PS_LOG_INFO, "Stage 6 : Final Stack : %f sec", psTimerClear("PPSTACK_STEPS"));
-    psLogMsg("ppStack", PS_LOG_INFO, "Time to make final stack: %f sec", psTimerClear("PPSTACK_FINAL"));
-
-    psTrace("ppStack", 2, "Cleaning up after combination....\n");
-
-#if 0
-    // Ensure masked regions really look masked
-    {
-        psString maskBadStr = psMetadataLookupStr(NULL, recipe, "MASK.BAD"); // Name of bits for bad
-        psImageMaskType maskBad = pmConfigMaskGet(maskBadStr, config); // Bits to mask for bad pixels
-        if (!pmReadoutMaskApply(outRO, maskBad)) {
-            psWarning("Unable to apply mask");
-        }
-    }
-#endif
-
-    // Close up
-    bool wcsDone = false;           // Have we done the WCS?
-    for (int i = 0; i < num; i++) {
-        if (inputMask->data.PS_TYPE_VECTOR_MASK_DATA[i]) {
-            continue;
-        }
-
-        ppStackThread *thread = stack->threads->data[0]; // Representative stack
-        pmReadout *inRO = thread->readouts->data[i]; // Template readout
-        if (inRO && !wcsDone) {
-            // Copy astrometry over
-            wcsDone = true;
-            pmHDU *inHDU = pmHDUFromCell(inRO->parent); // Template HDU
-            pmHDU *outHDU = pmHDUFromCell(outRO->parent); // Output HDU
-            pmChip *outChip = outRO->parent->parent; // Output chip
-            pmFPA *outFPA = outChip->parent; // Output FPA
-            if (!outHDU || !inHDU) {
-                psWarning("Unable to find HDU at FPA level to copy astrometry.");
-            } else {
-                if (!pmAstromReadWCS(outFPA, outChip, inHDU->header, 1.0)) {
-                    psErrorClear();
-                    psWarning("Unable to read WCS astrometry from input FPA.");
-                    wcsDone = false;
-                } else {
-                    if (!outHDU->header) {
-                        outHDU->header = psMetadataAlloc();
-                    }
-                    if (!pmAstromWriteWCS(outHDU->header, outFPA, outChip, WCS_TOLERANCE)) {
-                        psErrorClear();
-                        psWarning("Unable to write WCS astrometry to output FPA.");
-                        wcsDone = false;
-                    }
-                }
-            }
-        }
-
-        if (tempDelete) {
-            psString imageResolved = pmConfigConvertFilename(imageNames->data[i], config, false, false);
-            psString maskResolved = pmConfigConvertFilename(maskNames->data[i], config, false, false);
-            psString varianceResolved = pmConfigConvertFilename(varianceNames->data[i], config, false, false);
-            if (unlink(imageResolved) == -1 || unlink(maskResolved) == -1 ||
-                unlink(varianceResolved) == -1) {
-                psWarning("Unable to delete temporary files for image %d", i);
-            }
-            psFree(imageResolved);
-            psFree(maskResolved);
-            psFree(varianceResolved);
-        }
-    }
-    psFree(imageNames);
-    psFree(maskNames);
-    psFree(varianceNames);
-
-    psFree(inputMask);
     psFree(stack);
 
-    memDump("cleanup");
 
-    // Generate binned JPEGs
-    {
-        int bin1 = psMetadataLookupS32(NULL, recipe, "BIN1"); // First binning level
-        int bin2 = psMetadataLookupS32(NULL, recipe, "BIN2"); // Second binning level
-
-        // Target cells
-        pmCell *cell1 = pmFPAfileThisCell(config->files, view, "PPSTACK.OUTPUT.JPEG1");
-        pmCell *cell2 = pmFPAfileThisCell(config->files, view, "PPSTACK.OUTPUT.JPEG2");
-        psImageMaskType maskValue = pmConfigMaskGet("BLANK", config); // Bits to mask
-
-        pmReadout *ro1 = pmReadoutAlloc(cell1), *ro2 = pmReadoutAlloc(cell2); // Binned readouts
-        if (!pmReadoutRebin(ro1, outRO, maskValue, bin1, bin1) || !pmReadoutRebin(ro2, ro1, 0, bin2, bin2)) {
-            psError(PS_ERR_UNKNOWN, false, "Unable to bin output.");
-            psFree(ro1);
-            psFree(ro2);
-            psFree(outRO);
-            return false;
-        }
-        psFree(ro1);
-        psFree(ro2);
-    }
-
-    psLogMsg("ppStack", PS_LOG_INFO, "Stage 7 : WCS & JPEGS : %f sec", psTimerClear("PPSTACK_STEPS"));
-
-    if (psMetadataLookupBool(&mdok, recipe, "PHOTOMETRY")) {
-        psTrace("ppStack", 1, "Photometering stacked image....\n");
-
-        psTimerStart("PPSTACK_PHOT");
-
-        fileActivation(config, combineFiles, false);
-        fileActivation(config, photFiles, true);
-        pmFPAview *photView = filesIterateDown(config);
-        if (!ppStackPhotometry(config, outRO, photView)) {
-            psError(PS_ERR_UNKNOWN, false, "Unable to perform photometry on output.");
-            psFree(outRO);
-            psFree(photView);
-            return false;
-        }
-        psFree(photView);
-
-        fileActivation(config, combineFiles, true);
-
-        if (stats) {
-            pmFPAfile *photFile = psMetadataLookupPtr(NULL, config->files, "PSPHOT.INPUT"); // File
-            pmReadout *photRO = pmFPAviewThisReadout(view, photFile->fpa); // Readout with the sources
-            psArray *sources = psMetadataLookupPtr(NULL, photRO->analysis, "PSPHOT.SOURCES"); // Sources
-            psMetadataAddS32(stats, PS_LIST_TAIL, "NUM_SOURCES", 0, "Number of sources detected", sources->n);
-            psMetadataAddF32(stats, PS_LIST_TAIL, "TIME_PHOT", 0,
-                             "Time to do photometry", psTimerMark("PPSTACK_PHOT"));
-        }
-        psLogMsg("ppStack", PS_LOG_INFO, "Time to do photometry: %f sec", psTimerClear("PPSTACK_PHOT"));
-    }
-
-    psLogMsg("ppStack", PS_LOG_INFO, "Stage 8 : Photometry Analysis : %f sec", psTimerClear("PPSTACK_STEPS"));
-
-    psThreadPoolFinalize();
-
-    memDump("phot");
-
-    // Statistics on output
-    if (stats) {
-        psTrace("ppStack", 1, "Gathering statistics on stacked image....\n");
-        psString maskBadStr = psMetadataLookupStr(NULL, recipe, "MASK.BAD"); // Name of bits for bad
-        psImageMaskType maskBad = pmConfigMaskGet(maskBadStr, config); // Bits to mask for bad pixels
-
-        ppStatsFPA(stats, outRO->parent->parent->parent, view, maskBad, config);
-    }
-
-    memDump("stats");
-
-    // Put version information into the header
-    pmHDU *hdu = pmHDUFromCell(outRO->parent);
-    if (!hdu) {
-        psError(PS_ERR_UNKNOWN, false, "Unable to find HDU for output.");
-        psFree(outRO);
-        psFree(view);
+    // Photometry
+    psTrace("ppStack", 1, "Photometering stacked image....\n");
+    if (!ppStackPhotometry(options, config)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to perform photometry.");
+        psFree(options);
         return false;
     }
-    if (!hdu->header) {
-        hdu->header = psMetadataAlloc();
-    }
-    ppStackVersionMetadata(hdu->header);
+    psLogMsg("ppStack", PS_LOG_INFO, "Stage 8: Photometry Analysis: %f sec", psTimerClear("PPSTACK_STEPS"));
+    ppStackMemDump("photometry");
 
-    psFree(outRO);
-    psFree(view);
 
-    // Write out summary statistics
-    if (stats) {
-        psMetadataAddF32(stats, PS_LIST_TAIL, "TIME_STACK", 0,
-                         "Time to do photometry", psTimerClear("PPSTACK_TOTAL"));
-
-        const char *statsMDC = psMetadataConfigFormat(stats);
-        if (!statsMDC || strlen(statsMDC) == 0) {
-            psError(PS_ERR_IO, false, "Unable to get statistics MDC file.\n");
-        } else {
-            fprintf(statsFile, "%s", statsMDC);
-        }
-        psFree((void *)statsMDC);
-        fclose(statsFile);
-
-        psFree(stats);
-    }
-
-    // Write out the output files
-    if (!filesIterateUp(config)) {
+    // Finish up
+    psTrace("ppStack", 1, "Finishing up....\n");
+    if (!ppStackFinish(options, config)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to finish up.");
+        psFree(options);
         return false;
     }
+    psLogMsg("ppStack", PS_LOG_INFO, "Stage 9: Final output: %f sec", psTimerClear("PPSTACK_STEPS"));
+    ppStackMemDump("finish");
 
-    psLogMsg("ppStack", PS_LOG_INFO, "Stage 9 : Final Output : %f sec", psTimerClear("PPSTACK_STEPS"));
-
-    memDump("finish");
-
+    psFree(options);
     return true;
 }
Index: /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackLoop.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackLoop.h	(revision 23352)
+++ /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackLoop.h	(revision 23352)
@@ -0,0 +1,68 @@
+#ifndef PPSTACK_LOOP_H
+#define PPSTACK_LOOP_H
+
+#include <pslib.h>
+#include <psmodules.h>
+
+#include "ppStackOptions.h"
+#include "ppStackThread.h"
+
+
+// Loop over the inputs, doing the combination
+bool ppStackLoop(
+    pmConfig *config                    // Configuration
+    );
+
+// Setup
+bool ppStackSetup(
+    ppStackOptions *options,            // Options
+    pmConfig *config                    // Configuration
+    );
+
+// Preparation for stacking
+//
+// Merge input source lists, determine target PSF
+bool ppStackPrepare(
+    ppStackOptions *options,            // Options
+    pmConfig *config                    // Configuration
+    );
+
+// Convolve inputs to match target PSF
+bool ppStackConvolve(
+    ppStackOptions *options,            // Options
+    pmConfig *config                    // Configuration
+    );
+
+// Initial combination
+bool ppStackCombineInitial(
+    ppStackThreadData *stack,           // Stack
+    ppStackOptions *options,            // Options
+    pmConfig *config                    // Configuration
+    );
+
+// Reject pixels
+bool ppStackReject(
+    ppStackOptions *options,            // Options
+    pmConfig *config                    // Configuration
+    );
+
+// Final combination
+bool ppStackCombineFinal(
+    ppStackThreadData *stack,           // Stack
+    ppStackOptions *options,            // Options
+    pmConfig *config                    // Configuration
+    );
+
+// Photometry
+bool ppStackPhotometry(
+    ppStackOptions *options,            // Options
+    pmConfig *config                    // Configuration
+    );
+
+// Finish up
+bool ppStackFinish(
+    ppStackOptions *options,            // Options
+    pmConfig *config                    // Configuration
+    );
+
+#endif
Index: /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackMatch.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackMatch.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackMatch.c	(revision 23352)
@@ -145,5 +145,5 @@
 
     psImage *binned = psphotBackgroundModel(ro, config); // Binned background model
-    psImageBinning *binning = psMetadataLookupPtr(NULL, psphotRecipe,
+    psImageBinning *binning = psMetadataLookupPtr(NULL, ro->analysis,
                                                   "PSPHOT.BACKGROUND.BINNING"); // Binning for model
     psAssert(binning, "Need binning parameters");
@@ -358,4 +358,5 @@
                 psString name = NULL;
                 psStringAppend(&name, "fake_%03d.fits", numInput);
+                pmStackVisualPlotTestImage(fake->image, name);
                 psFits *fits = psFitsOpen(name, "w");
                 psFree(name);
@@ -367,4 +368,5 @@
                 psString name = NULL;
                 psStringAppend(&name, "real_%03d.fits", numInput);
+                pmStackVisualPlotTestImage(readout->image, name);
                 psFits *fits = psFitsOpen(name, "w");
                 psFree(name);
@@ -397,4 +399,5 @@
                 psString name = NULL;
                 psStringAppend(&name, "conv_%03d.fits", numInput);
+                pmStackVisualPlotTestImage(output->image, name);
                 psFits *fits = psFitsOpen(name, "w");
                 psFree(name);
@@ -406,4 +409,5 @@
                 psString name = NULL;
                 psStringAppend(&name, "diff_%03d.fits", numInput);
+                pmStackVisualPlotTestImage(fake->image, name);
                 psFits *fits = psFitsOpen(name, "w");
                 psFree(name);
@@ -633,4 +637,5 @@
         psString name = NULL;
         psStringAppend(&name, "convolved_%03d.fits", numInput);
+        pmStackVisualPlotTestImage(output->image, name);
         psFits *fits = psFitsOpen(name, "w");
         psFree(name);
Index: /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackOptions.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackOptions.c	(revision 23352)
+++ /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackOptions.c	(revision 23352)
@@ -0,0 +1,59 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pslib.h>
+#include "ppStackLoop.h"
+
+static void stackOptionsFree(ppStackOptions *options)
+{
+    psFree(options->stats);
+    if (options->statsFile) {
+        fclose(options->statsFile);
+    }
+    psFree(options->imageNames);
+    psFree(options->maskNames);
+    psFree(options->varianceNames);
+    psFree(options->psf);
+    psFree(options->inputMask);
+    psFree(options->sourceLists);
+    psFree(options->cells);
+    psFree(options->subKernels);
+    psFree(options->subRegions);
+    psFree(options->matchChi2);
+    psFree(options->weightings);
+    psFree(options->covariances);
+    psFree(options->outRO);
+    psFree(options->inspect);
+    psFree(options->rejected);
+    return;
+}
+
+ppStackOptions *ppStackOptionsAlloc(void)
+{
+    ppStackOptions *options = psAlloc(sizeof(ppStackOptions)); // Options, to return
+    psMemSetDeallocator(options, (psFreeFunc)stackOptionsFree);
+
+    options->stats = NULL;
+    options->statsFile = NULL;
+    options->imageNames = NULL;
+    options->maskNames = NULL;
+    options->varianceNames = NULL;
+    options->num = 0;
+    options->psf = NULL;
+    options->inputMask = NULL;
+    options->sourceLists = NULL;
+    options->cells = NULL;
+    options->subKernels = NULL;
+    options->subRegions = NULL;
+    options->numCols = 0;
+    options->numRows = 0;
+    options->matchChi2 = NULL;
+    options->weightings = NULL;
+    options->covariances = NULL;
+    options->outRO = NULL;
+    options->inspect = NULL;
+    options->rejected = NULL;
+
+    return options;
+}
Index: /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackOptions.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackOptions.h	(revision 23352)
+++ /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackOptions.h	(revision 23352)
@@ -0,0 +1,38 @@
+#ifndef PPSTACK_OPTIONS_H
+#define PPSTACK_OPTIONS_H
+
+#include <pslib.h>
+#include <psmodules.h>
+
+/// Options for stacking process
+typedef struct {
+    // Setup
+    psMetadata *stats;                  // Statistics for output
+    FILE *statsFile;                    // File to which to write statistics
+    psArray *imageNames, *maskNames, *varianceNames; // Filenames for the temporary convolved images
+    int num;                            // Number of inputs
+    // Prepare
+    pmPSF *psf;                         // Target PSF
+    float sumExposure;                  // Sum of exposure times
+    psVector *inputMask;                // Mask for inputs
+    psArray *sourceLists;               // Individual lists of sources for matching
+    // Convolve
+    psArray *cells;                     // Cells for convolved images --- a handle for reading again
+    psArray *subKernels;                // Subtraction kernels --- required in the stacking
+    psArray *subRegions;                // Subtraction regions --- required in the stacking
+    int numCols, numRows;               // Size of image
+    psVector *matchChi2;                // chi^2 for stamps from matching
+    psVector *weightings;               // Combination weightings for images (1/noise^2)
+    psArray *covariances;               // Covariance matrices
+    // Combine initial
+    pmReadout *outRO;                   // Output readout
+    psArray *inspect;                   // Array of arrays of pixels to inspect
+    // Rejection
+    psArray *rejected;                  // Rejected pixels
+} ppStackOptions;
+
+/// Allocator
+ppStackOptions *ppStackOptionsAlloc(void);
+
+
+#endif
Index: /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackPSF.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackPSF.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackPSF.c	(revision 23352)
@@ -9,5 +9,6 @@
 #include "ppStack.h"
 
-pmPSF *ppStackPSF(const pmConfig *config, int numCols, int numRows, const psArray *psfs)
+pmPSF *ppStackPSF(const pmConfig *config, int numCols, int numRows,
+                  const psArray *psfs, const psVector *inputMask)
 {
     // Get the recipe values
@@ -19,4 +20,11 @@
     const char *psfModel = psMetadataLookupStr(NULL, recipe, "PSF.MODEL"); // Model for PSF
     int psfOrder = psMetadataLookupS32(NULL, recipe, "PSF.ORDER"); // Spatial order for PSF
+
+    for (int i = 0; i < psfs->n; i++) {
+        if (inputMask->data.U8[i]) {
+            psFree(psfs->data[i]);
+            psfs->data[i] = NULL;
+        }
+    }
 
     // Solve for the target PSF
Index: /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackPhotometry.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackPhotometry.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackPhotometry.c	(revision 23352)
@@ -9,13 +9,10 @@
 
 #include "ppStack.h"
+#include "ppStackLoop.h"
 
-#define PHOT_SOURCE_MASK (PM_SOURCE_MODE_FAIL | PM_SOURCE_MODE_SATSTAR | PM_SOURCE_MODE_BLEND | \
-                          PM_SOURCE_MODE_BADPSF | PM_SOURCE_MODE_DEFECT | PM_SOURCE_MODE_SATURATED | \
-                          PM_SOURCE_MODE_CR_LIMIT | PM_SOURCE_MODE_EXT_LIMIT) // Mask to apply to sources
-
-bool ppStackInputPhotometry(const pmReadout *ro, const psArray *sources, const pmConfig *config)
+bool ppStackPhotometry(ppStackOptions *options, pmConfig *config)
 {
-    PM_ASSERT_READOUT_NON_NULL(ro, false);
-    PS_ASSERT_ARRAY_NON_NULL(sources, false);
+    psAssert(options, "Require options");
+    psAssert(config, "Require configuration");
 
     psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, PPSTACK_RECIPE); // ppStack recipe
@@ -23,105 +20,27 @@
 
     bool mdok;                          // Status of MD lookup
-
-    float zpRadius = psMetadataLookupS32(&mdok, recipe, "PHOT.RADIUS"); // Radius for PHOT measurement
-    if (!mdok) {
-        psError(PS_ERR_UNKNOWN, true, "Unable to find PHOT.RADIUS in recipe");
-        return false;
-    }
-    float zpSigma = psMetadataLookupF32(&mdok, recipe, "PHOT.SIGMA"); // Gaussian sigma for photometry
-    if (!mdok) {
-        psError(PS_ERR_UNKNOWN, true, "Unable to find PHOT.SIGMA in recipe");
-        return false;
-    }
-    float zpFrac = psMetadataLookupF32(&mdok, recipe, "PHOT.FRAC"); // Fraction of good pixels for photometry
-    if (!mdok) {
-        psError(PS_ERR_UNKNOWN, true, "Unable to find PHOT.FRAC in recipe");
-        return false;
+    if (!psMetadataLookupBool(&mdok, recipe, "PHOTOMETRY")) {
+        // Nothing to do
+        return true;
     }
 
-    psString maskValStr = psMetadataLookupStr(&mdok, recipe, "MASK.VAL"); // Name of bits to mask going in
-    if (!mdok || !maskValStr) {
-        psError(PS_ERR_UNKNOWN, false, "Unable to find MASK.VAL in recipe");
-        return false;
-    }
-    psImageMaskType maskVal = pmConfigMaskGet(maskValStr, config); // Bits to mask
+    psTimerStart("PPSTACK_PHOT");
 
-    psImage *image = ro->image, *mask = ro->mask; // Image and mask from readout
+    ppStackFileActivation(config, PPSTACK_FILES_COMBINE, false);
+    ppStackFileActivation(config, PPSTACK_FILES_PHOT, true);
+    pmFPAview *photView = ppStackFilesIterateDown(config); // View to readout
+    ppStackFileActivation(config, PPSTACK_FILES_COMBINE, true);
 
-    // Measure background
-    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 0); // Random number generator
-    psStats *stats = psStatsAlloc(PS_STAT_ROBUST_MEDIAN | PS_STAT_ROBUST_STDEV); // Statistics
-    if (!psImageBackground(stats, NULL, image, mask, maskVal, rng)) {
-        psError(PS_ERR_UNKNOWN, false, "Unable to measure background for image");
-        psFree(stats);
-        psFree(rng);
-        return false;
-    }
-    float bg = stats->robustMedian; // Background level
-    psFree(stats);
-    psFree(rng);
+    pmFPAfile *photFile = psMetadataLookupPtr(NULL, config->files, "PSPHOT.INPUT"); // File for photometry
+    pmFPACopy(photFile->fpa, options->outRO->parent->parent->parent);
 
-    // Photometer sources
-    int numCols = image->numCols, numRows = image->numRows; // Size of image
-    int numSources = sources->n; // Number of sources
-    int numGood = 0;            // Number of good sources
-    float maxMag = -INFINITY;   // Maximum magnitude
-    for (int j = 0; j < numSources; j++) {
-        pmSource *source = sources->data[j]; // Source of interest
-        if (source->mode & PHOT_SOURCE_MASK || !isfinite(source->psfMag)) {
-            source->psfMag = NAN;
-            continue;
-        }
-        float x = source->peak->xf, y = source->peak->yf; // Coordinates of source
-        int xCentral = x + 0.5, yCentral = y + 0.5; // Central pixel
-        // Range of integration
-        int xMin = PS_MAX(0, xCentral - zpRadius), xMax = PS_MIN(numCols - 1, xCentral + zpRadius);
-        int yMin = PS_MAX(0, yCentral - zpRadius), yMax = PS_MIN(numRows - 1, yCentral + zpRadius);
-
-        // Integrate
-        double sumImage = 0.0, sumKernel = 0.0; // Sums from integration
-        int numBadPix = 0;         // Number of bad pixels
-        for (int v = yMin; v <= yMax; v++) {
-            float dy2 = PS_SQR(y - v); // Distance from centroid
-            for (int u = xMin; u <= xMax; u++) {
-                if (mask->data.PS_TYPE_IMAGE_MASK_DATA[v][u] & maskVal) {
-                    numBadPix++;
-                    continue;
-                }
-                float dx2 = PS_SQR(x - u); // Distance from centroid
-                double kernel = exp(-0.5 * (dx2 + dy2) / PS_SQR(zpSigma)); // Kernel value
-                sumImage += (image->data.F32[v][u] - bg) * kernel;
-                sumKernel += kernel;
-            }
-        }
-
-        if (numBadPix > zpFrac * M_PI * PS_SQR(zpRadius) || !isfinite(sumImage)) {
-            source->psfMag = NAN;
-        } else {
-            source->psfMag = - 2.5 * log10(sumImage / sumKernel * M_PI * PS_SQR(zpRadius));
-            if (isfinite(source->psfMag)) {
-                numGood++;
-                maxMag = PS_MAX(maxMag, source->psfMag);
-            }
-        }
-    }
-    psLogMsg("ppStack", PS_LOG_INFO, "Photometered %d good sources in image; max mag %f", numGood, maxMag);
-
-    return true;
-}
-
-
-
-bool ppStackPhotometry(pmConfig *config, const pmReadout *readout, const pmFPAview *view)
-{
-    pmFPAfile *photFile = psMetadataLookupPtr(NULL, config->files, "PSPHOT.INPUT");
-    pmFPACopy(photFile->fpa, readout->parent->parent->parent);
-
-    if (psMetadataLookupBool(NULL, config->arguments, "-psphot-visual")) {
-        psphotSetVisual(true);
+    if (psMetadataLookupBool(NULL, config->arguments, "-visual")) {
+        pmVisualSetVisual(true);
     }
 
+    psMetadata *psphot = psMetadataLookupMetadata(NULL, config->recipes, PSPHOT_RECIPE); // Recipe
+
+#if 0
     // Need to ensure aperture residual is not calculated
-    psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, PSPHOT_RECIPE); // Recipe
     psMetadataItem *item = psMetadataLookup(recipe, "MEASURE.APTREND"); // Item determining aptrend
     if (!item) {
@@ -131,26 +50,29 @@
         item->data.B = false;
     }
+#endif
 
     // set maskValue and markValue in the psphot recipe
     psImageMaskType maskValue = pmConfigMaskGet("BLANK", config); // Bits to mask
     psImageMaskType markValue = pmConfigMaskGet("MARK.VALUE", config); // Bits to use for marking
-    psMetadataAddImageMask (recipe, PS_LIST_TAIL, "MASK.PSPHOT", PS_META_REPLACE, "Bits to mask", maskValue);
-    psMetadataAddImageMask (recipe, PS_LIST_TAIL, "MARK.PSPHOT", PS_META_REPLACE, "Bits to use for mark", markValue);
+    psMetadataAddImageMask(psphot, PS_LIST_TAIL, "MASK.PSPHOT", PS_META_REPLACE,
+                            "Bits to mask", maskValue);
+    psMetadataAddImageMask(psphot, PS_LIST_TAIL, "MARK.PSPHOT", PS_META_REPLACE,
+                           "Bits to use for mark", markValue);
 
-    // XXX replace with psphotReadoutKnownSources (config, view)
-    // XXX this function requires that we construct the input source list and place it on PSPHOT.INPUT.CMF
-    pmCell *sourcesCell = pmFPAfileThisCell(config->files, view, "PPSTACK.OUTPUT");
-    psArray *inSources = psMetadataLookupPtr (NULL, sourcesCell->analysis, "PSPHOT.SOURCES");
+    pmCell *sourcesCell = pmFPAfileThisCell(config->files, photView, "PPSTACK.OUTPUT");
+    psArray *inSources = psMetadataLookupPtr(NULL, sourcesCell->analysis, "PSPHOT.SOURCES");
     if (!inSources) {
         psError(PS_ERR_UNKNOWN, false, "Unable to find input sources");
+        psFree(photView);
         return false;
     }
 
-    if (!psphotReadoutKnownSources(config, view, inSources)) {
+    if (!psphotReadoutKnownSources(config, photView, inSources)) {
         // Clear the error, so that the output files are written.
-        psWarning("Unable to perform photometry on stacked image.");
-        psErrorStackPrint(stderr, "Error stack from photometry:");
-        psErrorClear();
+        psError(PS_ERR_UNKNOWN, false, "Unable to perform photometry on stacked image.");
+        psFree(photView);
+        return false;
     }
+    psFree(photView);
 
     if (!pmFPAfileDropInternal(config->files, "PSPHOT.BACKMDL") ||
@@ -163,4 +85,14 @@
     pmFPAfileActivate(config->files, false, "PSPHOT.INPUT");
 
+    if (options->stats) {
+        pmReadout *photRO = pmFPAviewThisReadout(photView, photFile->fpa); // Readout with the sources
+        psArray *sources = psMetadataLookupPtr(NULL, photRO->analysis, "PSPHOT.SOURCES"); // Sources
+        psMetadataAddS32(options->stats, PS_LIST_TAIL, "NUM_SOURCES", 0,
+                         "Number of sources detected", sources->n);
+        psMetadataAddF32(options->stats, PS_LIST_TAIL, "TIME_PHOT", 0,
+                         "Time to do photometry", psTimerMark("PPSTACK_PHOT"));
+    }
+    psLogMsg("ppStack", PS_LOG_INFO, "Time to do photometry: %f sec", psTimerClear("PPSTACK_PHOT"));
+
     return true;
 }
Index: /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackPrepare.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackPrepare.c	(revision 23352)
+++ /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackPrepare.c	(revision 23352)
@@ -0,0 +1,251 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <pslib.h>
+#include <psmodules.h>
+
+#include "ppStack.h"
+#include "ppStackLoop.h"
+
+#define PHOT_SOURCE_MASK (PM_SOURCE_MODE_FAIL | PM_SOURCE_MODE_SATSTAR | PM_SOURCE_MODE_BLEND | \
+                          PM_SOURCE_MODE_BADPSF | PM_SOURCE_MODE_DEFECT | PM_SOURCE_MODE_SATURATED | \
+                          PM_SOURCE_MODE_CR_LIMIT | PM_SOURCE_MODE_EXT_LIMIT) // Mask to apply to sources
+
+bool ppStackInputPhotometer(const pmReadout *ro, const psArray *sources, const pmConfig *config)
+{
+    PM_ASSERT_READOUT_NON_NULL(ro, false);
+    PS_ASSERT_ARRAY_NON_NULL(sources, false);
+
+    psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, PPSTACK_RECIPE); // ppStack recipe
+    psAssert(recipe, "We've thrown an error on this before.");
+
+    bool mdok;                          // Status of MD lookup
+
+    float zpRadius = psMetadataLookupS32(&mdok, recipe, "PHOT.RADIUS"); // Radius for PHOT measurement
+    if (!mdok) {
+        psError(PS_ERR_UNKNOWN, true, "Unable to find PHOT.RADIUS in recipe");
+        return false;
+    }
+    float zpSigma = psMetadataLookupF32(&mdok, recipe, "PHOT.SIGMA"); // Gaussian sigma for photometry
+    if (!mdok) {
+        psError(PS_ERR_UNKNOWN, true, "Unable to find PHOT.SIGMA in recipe");
+        return false;
+    }
+    float zpFrac = psMetadataLookupF32(&mdok, recipe, "PHOT.FRAC"); // Fraction of good pixels for photometry
+    if (!mdok) {
+        psError(PS_ERR_UNKNOWN, true, "Unable to find PHOT.FRAC in recipe");
+        return false;
+    }
+
+    psString maskValStr = psMetadataLookupStr(&mdok, recipe, "MASK.VAL"); // Name of bits to mask going in
+    if (!mdok || !maskValStr) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to find MASK.VAL in recipe");
+        return false;
+    }
+    psImageMaskType maskVal = pmConfigMaskGet(maskValStr, config); // Bits to mask
+
+    psImage *image = ro->image, *mask = ro->mask; // Image and mask from readout
+
+    // Measure background
+    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS); // Random number generator
+    psStats *stats = psStatsAlloc(PS_STAT_ROBUST_MEDIAN | PS_STAT_ROBUST_STDEV); // Statistics
+    if (!psImageBackground(stats, NULL, image, mask, maskVal, rng)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to measure background for image");
+        psFree(stats);
+        psFree(rng);
+        return false;
+    }
+    float bg = stats->robustMedian; // Background level
+    psFree(stats);
+    psFree(rng);
+
+    // Photometer sources
+    int numCols = image->numCols, numRows = image->numRows; // Size of image
+    int numSources = sources->n; // Number of sources
+    int numGood = 0;            // Number of good sources
+    float maxMag = -INFINITY;   // Maximum magnitude
+    for (int j = 0; j < numSources; j++) {
+        pmSource *source = sources->data[j]; // Source of interest
+        if (source->mode & PHOT_SOURCE_MASK || !isfinite(source->psfMag)) {
+            source->psfMag = NAN;
+            continue;
+        }
+        float x = source->peak->xf, y = source->peak->yf; // Coordinates of source
+        int xCentral = x + 0.5, yCentral = y + 0.5; // Central pixel
+        // Range of integration
+        int xMin = PS_MAX(0, xCentral - zpRadius), xMax = PS_MIN(numCols - 1, xCentral + zpRadius);
+        int yMin = PS_MAX(0, yCentral - zpRadius), yMax = PS_MIN(numRows - 1, yCentral + zpRadius);
+
+        // Integrate
+        double sumImage = 0.0, sumKernel = 0.0; // Sums from integration
+        int numBadPix = 0;         // Number of bad pixels
+        for (int v = yMin; v <= yMax; v++) {
+            float dy2 = PS_SQR(y - v); // Distance from centroid
+            for (int u = xMin; u <= xMax; u++) {
+                if (mask->data.PS_TYPE_IMAGE_MASK_DATA[v][u] & maskVal) {
+                    numBadPix++;
+                    continue;
+                }
+                float dx2 = PS_SQR(x - u); // Distance from centroid
+                double kernel = exp(-0.5 * (dx2 + dy2) / PS_SQR(zpSigma)); // Kernel value
+                sumImage += (image->data.F32[v][u] - bg) * kernel;
+                sumKernel += kernel;
+            }
+        }
+
+        if (numBadPix > zpFrac * M_PI * PS_SQR(zpRadius) || !isfinite(sumImage)) {
+            source->psfMag = NAN;
+        } else {
+            source->psfMag = - 2.5 * log10(sumImage / sumKernel * M_PI * PS_SQR(zpRadius));
+            if (isfinite(source->psfMag)) {
+                numGood++;
+                maxMag = PS_MAX(maxMag, source->psfMag);
+            }
+        }
+    }
+    psLogMsg("ppStack", PS_LOG_INFO, "Photometered %d good sources in image; max mag %f", numGood, maxMag);
+
+    return true;
+}
+
+
+// Preparation iteration: Load the sources, and get a target PSF model
+bool ppStackPrepare(ppStackOptions *options, pmConfig *config)
+{
+    psAssert(options, "Require options");
+    psAssert(config, "Require configuration");
+
+    psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, PPSTACK_RECIPE); // ppStack recipe
+    psAssert(recipe, "We've thrown an error on this before.");
+
+    int num = options->num = psMetadataLookupS32(NULL, config->arguments, "INPUTS.NUM");
+    options->sourceLists = psArrayAlloc(num); // Individual lists of sources for matching
+
+    options->inputMask = psVectorAlloc(num, PS_TYPE_VECTOR_MASK); // Mask for inputs
+    psVectorInit(options->inputMask, 0);
+
+    if (psMetadataLookupBool(NULL, config->arguments, "HAVE.PSF")) {
+        pmFPAfileActivate(config->files, false, NULL);
+        ppStackFileActivation(config, PPSTACK_FILES_PREPARE, true);
+        pmFPAview *view = ppStackFilesIterateDown(config);
+        if (!view) {
+            return false;
+        }
+
+        // Generate list of PSFs
+        psMetadataIterator *fileIter = psMetadataIteratorAlloc(config->files, PS_LIST_HEAD,
+                                                               "^PPSTACK.INPUT$"); // Iterator
+        psMetadataItem *fileItem; // Item from iteration
+        psArray *psfs = psArrayAlloc(num); // PSFs for PSF envelope
+        int numCols = 0, numRows = 0;   // Size of image
+        int index = 0;                  // Index for file
+        while ((fileItem = psMetadataGetAndIncrement(fileIter))) {
+            assert(fileItem->type == PS_DATA_UNKNOWN);
+            pmFPAfile *inputFile = fileItem->data.V; // An input file
+            pmChip *chip = pmFPAviewThisChip(view, inputFile->fpa); // The chip: holds the PSF
+            pmPSF *psf = psMetadataLookupPtr(NULL, chip->analysis, "PSPHOT.PSF"); // PSF
+            if (!psf) {
+                psError(PS_ERR_UNKNOWN, false, "Unable to find PSF.");
+                psFree(view);
+                psFree(fileIter);
+                psFree(psfs);
+                return false;
+            }
+            psfs->data[index] = psMemIncrRefCounter(psf);
+            psMetadataRemoveKey(chip->analysis, "PSPHOT.PSF");
+
+            pmCell *cell = pmFPAviewThisCell(view, inputFile->fpa); // Cell of interest
+            pmHDU *hdu = pmHDUFromCell(cell);
+            assert(hdu && hdu->header);
+            int naxis1 = psMetadataLookupS32(NULL, hdu->header, "NAXIS1"); // Number of columns
+            int naxis2 = psMetadataLookupS32(NULL, hdu->header, "NAXIS2"); // Number of rows
+            if (naxis1 <= 0 || naxis2 <= 0) {
+                psError(PS_ERR_UNKNOWN, false, "Unable to determine size of image from PSF.");
+                psFree(view);
+                psFree(fileIter);
+                psFree(psfs);
+                return false;
+            }
+            if (numCols == 0 && numRows == 0) {
+                numCols = naxis1;
+                numRows = naxis2;
+            }
+
+            pmReadout *ro = pmFPAviewThisReadout(view, inputFile->fpa); // Readout with sources
+            psArray *sources = psMetadataLookupPtr(NULL, ro->analysis, "PSPHOT.SOURCES"); // Sources
+            if (!sources) {
+                psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to find sources in readout.");
+                return NULL;
+            }
+            options->sourceLists->data[index] = psMemIncrRefCounter(sources);
+
+            // Re-do photometry if we don't trust the source lists
+            if (psMetadataLookupBool(NULL, recipe, "PHOT")) {
+                psTrace("ppStack", 2, "Photometering input %d of %d....\n", index, num);
+                pmFPAfileActivate(config->files, false, NULL);
+                ppStackFileActivationSingle(config, PPSTACK_FILES_CONVOLVE, true, index);
+                pmFPAfile *file = pmFPAfileSelectSingle(config->files, "PPSTACK.INPUT", index); // File
+                pmFPAview *photView = ppStackFilesIterateDown(config);
+                if (!photView) {
+                    psFree(view);
+                    return false;
+                }
+
+                pmReadout *ro = pmFPAviewThisReadout(view, file->fpa); // Readout of interest
+
+                if (!ppStackInputPhotometer(ro, sources, config)) {
+                    psError(PS_ERR_UNKNOWN, false, "Unable to do photometry on input sources");
+                    psFree(view);
+                    psFree(photView);
+                    return false;
+                }
+
+                psFree(photView);
+                if (!ppStackFilesIterateUp(config)) {
+                    psFree(view);
+                    return false;
+                }
+                pmFPAfileActivate(config->files, false, NULL);
+                ppStackFileActivation(config, PPSTACK_FILES_PREPARE, true);
+            }
+
+            index++;
+        }
+        psFree(fileIter);
+
+        // Zero point calibration
+        options->sumExposure = ppStackSourcesTransparency(options->sourceLists, options->inputMask,
+                                                          view, config);
+        if (!isfinite(options->sumExposure) || options->sumExposure <= 0) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to calculate transparency differences");
+            psFree(view);
+            return false;
+        }
+
+        // Generate target PSF
+        options->psf = ppStackPSF(config, numCols, numRows, psfs, options->inputMask);
+        psFree(psfs);
+        if (!options->psf) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to determine output PSF.");
+            psFree(view);
+            return false;
+        }
+        psMetadataAddPtr(config->arguments, PS_LIST_TAIL, "PSF.TARGET", PS_DATA_UNKNOWN,
+                         "Target PSF for stack", options->psf);
+
+        pmChip *outChip = pmFPAfileThisChip(config->files, view, "PPSTACK.OUTPUT"); // Output chip
+        psMetadataAddPtr(outChip->analysis, PS_LIST_TAIL, "PSPHOT.PSF", PS_DATA_UNKNOWN,
+                         "Target PSF", options->psf);
+        outChip->data_exists = true;
+
+        psFree(view);
+
+        if (!ppStackFilesIterateUp(config)) {
+            return false;
+        }
+    }
+
+    return true;
+}
Index: /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackReadout.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackReadout.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackReadout.c	(revision 23352)
@@ -8,4 +8,5 @@
 #include <psphot.h>
 
+#include "ppStackThread.h"
 #include "ppStack.h"
 
@@ -16,9 +17,11 @@
     psArray *args = job->args;          // Arguments
     ppStackThread *thread = args->data[0]; // Thread
-    pmConfig *config = args->data[1];   // Configuration
-    pmReadout *outRO = args->data[2];   // Output readout
-    psVector *mask = args->data[3];     // Mask for inputs
-    psVector *weightings = args->data[4]; // Weightings (1/noise^2) for each image
-    psVector *addVariance = args->data[5]; // Additional variance when rejecting
+    ppStackOptions *options = args->data[1]; // Options
+    pmConfig *config = args->data[2];   // Configuration
+
+    pmReadout *outRO = options->outRO;  // Output readout
+    psVector *mask = options->inputMask; // Mask for inputs
+    psVector *weightings = options->weightings; // Weightings (1/noise^2) for each image
+    psVector *addVariance = options->matchChi2; // Additional variance when rejecting
 
     psArray *inspect = ppStackReadoutInitial(config, outRO, thread->readouts, mask,
@@ -37,10 +40,12 @@
     psArray *args = job->args;          // Arguments
     ppStackThread *thread = args->data[0]; // Thread
-    pmConfig *config = args->data[1];   // Configuration
-    pmReadout *outRO = args->data[2];   // Output readout
-    psVector *mask = args->data[3];     // Mask for inputs
-    psArray *rejected = args->data[4];  // Rejected pixels
-    psVector *weightings = args->data[5];  // Weighting (1/noise^2) for each image
-    psVector *addVariance = args->data[6];  // Weighting (1/noise^2) for each image
+    ppStackOptions *options = args->data[1]; // Options
+    pmConfig *config = args->data[2];   // Configuration
+
+    pmReadout *outRO = options->outRO;  // Output readout
+    psVector *mask = options->inputMask; // Mask for inputs
+    psArray *rejected = options->rejected; // Rejected pixels
+    psVector *weightings = options->weightings; // Weightings (1/noise^2) for each image
+    psVector *addVariance = options->matchChi2; // Additional variance when rejecting
 
     bool status = ppStackReadoutFinal(config, outRO, thread->readouts, mask, rejected,
@@ -227,5 +232,6 @@
         }
 
-        pmStackData *data = pmStackDataAlloc(ro, weightings->data.F32[i], addVariance->data.F32[i]);
+        pmStackData *data = pmStackDataAlloc(ro, weightings->data.F32[i],
+                                             addVariance ? addVariance->data.F32[i] : NAN);
         data->reject = psMemIncrRefCounter(rejected->data[i]);
         stack->data[i] = data;
Index: /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackReject.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackReject.c	(revision 23352)
+++ /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackReject.c	(revision 23352)
@@ -0,0 +1,157 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <pslib.h>
+#include <psmodules.h>
+
+#include "ppStack.h"
+#include "ppStackLoop.h"
+
+bool ppStackReject(ppStackOptions *options, pmConfig *config)
+{
+    psAssert(options, "Require options");
+    psAssert(config, "Require configuration");
+
+    int num = options->num;             // Number of inputs
+    options->rejected = psArrayAlloc(num);
+
+    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(ppsub, "We've thrown an error on this before.");
+
+    float threshold = psMetadataLookupF32(NULL, recipe, "THRESHOLD.MASK"); // Threshold for mask deconvolution
+    float poorFrac = psMetadataLookupF32(NULL, recipe, "POOR.FRACTION"); // Fraction for "poor"
+    float imageRej = psMetadataLookupF32(NULL, recipe, "IMAGE.REJ"); // Maximum fraction of image to reject
+                                                                     // before rejecting entire image
+    int stride = psMetadataLookupS32(NULL, ppsub, "STRIDE"); // Size of convolution patches
+
+    // Count images rejected out of hand
+    int numRejected = 0;        // Number of inputs rejected completely
+    for (int i = 0; i < num; i++) {
+        if (options->inputMask->data.PS_TYPE_VECTOR_MASK_DATA[i]) {
+            numRejected++;
+        }
+    }
+
+    // Concatenate inspection lists
+    for (int i = 0; i < num; i++) {
+        if (options->inputMask->data.PS_TYPE_VECTOR_MASK_DATA[i]) {
+            continue;
+        }
+
+        psThreadJob *job = psThreadJobAlloc("PPSTACK_INSPECT"); // Job to start
+        psArrayAdd(job->args, 1, options->inspect);
+        PS_ARRAY_ADD_SCALAR(job->args, i, PS_TYPE_S32);
+        if (!psThreadJobAddPending(job)) {
+            psFree(job);
+            return false;
+        }
+        psFree(job);
+    }
+    if (!psThreadPoolWait(true)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to concatenate inspection lists.");
+        return false;
+    }
+
+
+    // Reject bad pixels
+    if (psMetadataLookupS32(NULL, config->arguments, "-threads") > 0) {
+        pmStackRejectThreadsInit();
+    }
+    for (int i = 0; i < num; i++) {
+        if (options->inputMask->data.PS_TYPE_VECTOR_MASK_DATA[i]) {
+            continue;
+        }
+        psTimerStart("PPSTACK_REJECT");
+
+#ifdef TESTING
+        {
+            psImage *mask = psPixelsToMask(NULL, options->inspect->data[i],
+                                           psRegionSet(0, options->numCols - 1, 0, options->numRows - 1),
+                                           0xff); // Mask image
+            psString name = NULL;           // Name of image
+            psStringAppend(&name, "inspect_%03d.fits", i);
+            pmStackVisualPlotTestImage(mask, name);
+            psFits *fits = psFitsOpen(name, "w");
+            psFree(name);
+            psFitsWriteImage(fits, NULL, mask, 0, NULL);
+            psFree(mask);
+            psFitsClose(fits);
+        }
+#endif
+
+        psPixels *reject = pmStackReject(options->inspect->data[i], options->numCols, options->numRows,
+                                         threshold, poorFrac, stride, options->subRegions->data[i],
+                                         options->subKernels->data[i]); // Rejected pixels
+
+#ifdef TESTING
+        {
+            psImage *mask = psPixelsToMask(NULL, reject,
+                                           psRegionSet(0, options->numCols - 1, 0, options->numRows - 1),
+                                           0xff); // Mask image
+            psString name = NULL;           // Name of image
+            psStringAppend(&name, "reject_%03d.fits", i);
+            pmStackVisualPlotTestImage(mask, name);
+            psFits *fits = psFitsOpen(name, "w");
+            psFree(name);
+            psFitsWriteImage(fits, NULL, mask, 0, NULL);
+            psFree(mask);
+            psFitsClose(fits);
+        }
+#endif
+
+        psFree(options->inspect->data[i]);
+        options->inspect->data[i] = NULL;
+
+        if (!reject) {
+            psWarning("Rejection on image %d didn't work --- reject entire image.", i);
+            numRejected++;
+            options->inputMask->data.PS_TYPE_VECTOR_MASK_DATA[i] = PPSTACK_MASK_REJECT;
+        } else {
+            float frac = reject->n / (float)(options->numCols * options->numRows); // Pixel fraction
+            psLogMsg("ppStack", PS_LOG_INFO, "%ld pixels rejected from image %d (%.1f%%)",
+                     reject->n, i, frac * 100.0);
+            if (frac > imageRej) {
+                psWarning("Image %d rejected completely because rejection fraction (%.3f) "
+                          "exceeds limit (%.3f)", i, frac, imageRej);
+                psFree(reject);
+                // reject == NULL means reject image completely
+                reject = NULL;
+                options->inputMask->data.PS_TYPE_VECTOR_MASK_DATA[i] |= PPSTACK_MASK_BAD;
+                numRejected++;
+            }
+        }
+
+        // Images without a list of rejected pixels (the list may be empty) are rejected completely
+        options->rejected->data[i] = reject;
+
+        if (options->stats) {
+            psMetadataAddF32(options->stats, PS_LIST_TAIL, "TIME_REJECT", PS_META_DUPLICATE_OK,
+                             "Time to perform rejection", psTimerMark("PPSTACK_REJECT"));
+            psMetadataAddS32(options->stats, PS_LIST_TAIL, "REJECT_PIXELS", PS_META_DUPLICATE_OK,
+                             "Number of pixels rejected", reject ? reject->n : 0);
+        }
+        psLogMsg("ppStack", PS_LOG_INFO, "Time to perform rejection on image %d: %f sec", i,
+                 psTimerClear("PPSTACK_REJECT"));
+    }
+
+    psFree(options->inspect); options->inspect = NULL;
+    psFree(options->subKernels); options->subKernels = NULL;
+    psFree(options->subRegions); options->subRegions = NULL;
+
+    if (numRejected >= num - 1) {
+        psError(PS_ERR_UNKNOWN, true, "All inputs completely rejected; unable to proceed.");
+        return false;
+    }
+
+    if (options->stats) {
+        psMetadataAddS32(options->stats, PS_LIST_TAIL, "REJECT_IMAGES", 0,
+                         "Number of images rejected completely", numRejected);
+    }
+
+    return true;
+}
Index: /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackSetup.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackSetup.c	(revision 23352)
+++ /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackSetup.c	(revision 23352)
@@ -0,0 +1,84 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <libgen.h>
+#include <pslib.h>
+#include <psmodules.h>
+
+#include "ppStack.h"
+#include "ppStackLoop.h"
+
+bool ppStackSetup(ppStackOptions *options, pmConfig *config)
+{
+    psAssert(options, "Require options");
+    psAssert(config, "Require configuration");
+
+    psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, PPSTACK_RECIPE); // ppStack recipe
+    psAssert(recipe, "We've thrown an error on this before.");
+
+    int num = psMetadataLookupS32(NULL, config->arguments, "INPUTS.NUM"); // Number of inputs
+    options->num = num;
+
+    bool mdok;                          // Status of MD lookup
+    const char *statsName = psMetadataLookupStr(&mdok, config->arguments, "STATS"); // Filename for statistics
+    if (statsName && strlen(statsName) > 0) {
+        psString resolved = pmConfigConvertFilename(statsName, config, true, true); // Resolved filename
+        options->statsFile = fopen(resolved, "w");
+        if (!options->statsFile) {
+            psError(PS_ERR_IO, true, "Unable to open statistics file %s for writing.\n", resolved);
+            psFree(resolved);
+            return false;
+        }
+        psFree(resolved);
+        options->stats = psMetadataAlloc();
+    }
+
+    const char *tempDir = psMetadataLookupStr(NULL, recipe, "TEMP.DIR"); // Directory for temporary images
+    if (!tempDir) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false, "Unable to find TEMP.DIR in recipe");
+        return false;
+    }
+
+    psString outputName = psStringCopy(psMetadataLookupStr(NULL, config->arguments,
+                                                           "OUTPUT")); // Name for temporary files
+    const char *tempName = basename(outputName);
+    if (!tempName) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false, "Unable to construct basename for temporary files.");
+        psFree(outputName);
+        return false;
+    }
+
+    const char *tempImage = psMetadataLookupStr(NULL, recipe, "TEMP.IMAGE"); // Suffix for temporary images
+    const char *tempMask = psMetadataLookupStr(NULL, recipe, "TEMP.MASK"); // Suffix for temporary masks
+    const char *tempVariance = psMetadataLookupStr(NULL, recipe, "TEMP.VARIANCE"); // Suffix for temp var maps
+    if (!tempImage || !tempMask || !tempVariance) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, false,
+                "Unable to find TEMP.IMAGE, TEMP.MASK and TEMP.VARIANCE in recipe");
+        psFree(outputName);
+        return false;
+    }
+
+    options->imageNames = psArrayAlloc(num);
+    options->maskNames = psArrayAlloc(num);
+    options->varianceNames = psArrayAlloc(num);
+    for (int i = 0; i < num; i++) {
+        psString imageName = NULL, maskName = NULL, varianceName = NULL; // Names for convolved images
+        psStringAppend(&imageName, "%s/%s.%d.%s", tempDir, tempName, i, tempImage);
+        psStringAppend(&maskName, "%s/%s.%d.%s", tempDir, tempName, i, tempMask);
+        psStringAppend(&varianceName, "%s/%s.%d.%s", tempDir, tempName, i, tempVariance);
+        psTrace("ppStack", 5, "Temporary files: %s %s %s\n", imageName, maskName, varianceName);
+        options->imageNames->data[i] = imageName;
+        options->maskNames->data[i] = maskName;
+        options->varianceNames->data[i] = varianceName;
+    }
+    psFree(outputName);
+
+    if (!pmConfigMaskSetBits(NULL, NULL, config)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to determine mask value.");
+        return false;
+    }
+
+    return true;
+}
Index: /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackSources.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackSources.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackSources.c	(revision 23352)
@@ -13,11 +13,55 @@
 #define FAKE_ROWS 4913
 
-float ppStackSourcesTransparency(const psArray *sourceLists, const pmFPAview *view, const pmConfig *config)
+#ifdef TESTING
+// Dump matches to a file
+static void dumpMatches(const char *filename, // File to which to dump
+                        int num,        // Number of inputs
+                        psArray *matches, // Star matches
+                        psVector *zp,   // Zero points
+                        psVector *trans // Transparencies
+                        )
+{
+    FILE *outMatches = fopen(filename, "w"); // Output matches
+    psVector *mag = psVectorAlloc(num, PS_TYPE_F32); // Magnitudes for each star
+    psVector *magErr = psVectorAlloc(num, PS_TYPE_F32); // Errors for each star
+    for (int i = 0; i < matches->n; i++) {
+        pmSourceMatch *match = matches->data[i]; // Match of interest
+        psVectorInit(mag, NAN);
+        psVectorInit(magErr, NAN);
+        for (int j = 0; j < match->num; j++) {
+            if (match->mask->data.PS_TYPE_VECTOR_MASK_DATA[j]) {
+                continue;
+            }
+            int index = match->image->data.U32[j]; // Image index
+            mag->data.F32[index] = match->mag->data.F32[j] - zp->data.F32[index];
+            if (trans) {
+                mag->data.F32[index] -= trans->data.F32[index];
+            }
+            magErr->data.F32[index] = match->magErr->data.F32[j];
+        }
+        for (int j = 0; j < num; j++) {
+            fprintf(outMatches, "%f (%f) ", mag->data.F32[j], magErr->data.F32[j]);
+        }
+        fprintf(outMatches, "\n");
+    }
+    psFree(mag);
+    psFree(magErr);
+    fclose(outMatches);
+    return;
+}
+#endif
+
+
+float ppStackSourcesTransparency(const psArray *sourceLists, psVector *inputMask,
+                                 const pmFPAview *view, const pmConfig *config)
 {
     PS_ASSERT_ARRAY_NON_NULL(sourceLists, NAN);
+    PS_ASSERT_VECTOR_NON_NULL(inputMask, NAN);
+    PS_ASSERT_VECTOR_TYPE(inputMask, PS_TYPE_U8, NAN);
+    PS_ASSERT_VECTOR_SIZE(inputMask, sourceLists->n, NAN);
     PS_ASSERT_PTR_NON_NULL(view, NAN);
     PS_ASSERT_PTR_NON_NULL(config, NAN);
 
-#ifdef TESTING
+#if defined(TESTING) && 0
     {
         // Deliberately induce a major transparency difference
@@ -37,12 +81,19 @@
 
     float radius = psMetadataLookupF32(NULL, recipe, "ZP.RADIUS"); // Radius (pixels) for matching sources
-    int iter = psMetadataLookupS32(NULL, recipe, "ZP.ITER"); // Maximum iterations
+    int iter1 = psMetadataLookupS32(NULL, recipe, "ZP.ITER.1"); // Maximum iterations for pass 1
+    int iter2 = psMetadataLookupS32(NULL, recipe, "ZP.ITER.2"); // Maximum iterations for pass 2
     float tol = psMetadataLookupF32(NULL, recipe, "ZP.TOL"); // Tolerance for zero point iterations
     int transIter = psMetadataLookupS32(NULL, recipe, "ZP.TRANS.ITER"); // Iterations for transparency
     float transRej = psMetadataLookupF32(NULL, recipe, "ZP.TRANS.REJ");// Rejection threshold for transparency
     float transThresh = psMetadataLookupF32(NULL, recipe, "ZP.TRANS.THRESH"); // Threshold for transparency
-    float starRej = psMetadataLookupF32(NULL, recipe, "ZP.STAR.REJ"); // Rejection threshold for stars
+
+    float starRej1 = psMetadataLookupF32(NULL, recipe, "ZP.STAR.REJ.1"); // Rejection threshold for stars
+    float starSys1 = psMetadataLookupF32(NULL, recipe, "ZP.STAR.SYS.1"); // Estimated systematic error
+    float starRej2 = psMetadataLookupF32(NULL, recipe, "ZP.STAR.REJ.2"); // Rejection threshold for stars
+    float starSys2 = psMetadataLookupF32(NULL, recipe, "ZP.STAR.SYS.2"); // Estimated systematic error
+
     float starLimit = psMetadataLookupF32(NULL, recipe, "ZP.STAR.LIMIT"); // Limit on star rejection fraction
-    float starSys = psMetadataLookupF32(NULL, recipe, "ZP.STAR.SYS"); // Estimated systematic error
+
+    float fracMatch = psMetadataLookupF32(NULL, recipe, "ZP.MATCH"); // Fraction of images to match for star
 
     psMetadata *airmassZP = psMetadataLookupMetadata(NULL, recipe, "ZP.AIRMASS"); // Airmass terms
@@ -63,10 +114,12 @@
         pmCell *cell = pmFPAviewThisCell(view, file->fpa); // Cell of interest
 
-#ifdef TESTING
+#if defined(TESTING) && 0
         pmReadout *fake = pmReadoutAlloc(NULL); // Fake readout
         pmPSF *psf = psMetadataLookupPtr(NULL, config->arguments, "PSF.TARGET"); // PSF for fake image
-        pmReadoutFakeFromSources(fake, FAKE_COLS, FAKE_ROWS, sourceLists->data[i], NULL, NULL, psf, 5, 0, false, true);
+        pmReadoutFakeFromSources(fake, FAKE_COLS, FAKE_ROWS, sourceLists->data[i],
+                                 NULL, NULL, psf, 5, 0, false, true);
         psString name = NULL;
         psStringAppend(&name, "start_%03d.fits", i);
+        pmStackVisualPlotTestImage(fake->image, name);
         psFits *fits = psFitsOpen(name, "w");
         psFree(name);
@@ -108,5 +161,5 @@
     }
 
-    psArray *matches = pmSourceMatchSources(sourceLists, radius); // List of matches
+    psArray *matches = pmSourceMatchSources(sourceLists, radius, true); // List of matches
     if (!matches) {
         psError(PS_ERR_UNKNOWN, false, "Unable to match sources");
@@ -114,65 +167,54 @@
         return NAN;
     }
-    psVector *trans = pmSourceMatchRelphot(matches, zp, iter, tol, starLimit, transIter, transRej,
-                                           transThresh, starRej, starSys); // Transparencies for each image
-
-#ifdef TESTING
-    {
-        // Dump the corrected magnitudes
-        FILE *outMatches = fopen("source_match.dat", "w"); // Output matches
-        psVector *mag = psVectorAlloc(num, PS_TYPE_F32); // Magnitudes for each star
-        psVector *magErr = psVectorAlloc(num, PS_TYPE_F32); // Errors for each star
-        for (int i = 0; i < matches->n; i++) {
-            pmSourceMatch *match = matches->data[i]; // Match of interest
-            psVectorInit(mag, NAN);
-            psVectorInit(magErr, NAN);
-            for (int j = 0; j < match->num; j++) {
-                if (match->mask->data.PS_TYPE_VECTOR_MASK_DATA[j]) {
-                    continue;
-                }
-                int index = match->image->data.U32[j]; // Image index
-                mag->data.F32[index] = match->mag->data.F32[j] - zp->data.F32[index] - trans->data.F32[index];
-                magErr->data.F32[index] = match->magErr->data.F32[j];
-            }
-            for (int j = 0; j < num; j++) {
-                fprintf(outMatches, "%f (%f) ", mag->data.F32[j], magErr->data.F32[j]);
-            }
-            fprintf(outMatches, "\n");
-        }
-        psFree(mag);
-        psFree(magErr);
-        fclose(outMatches);
-    }
-#endif
+
+#ifdef TESTING
+    dumpMatches("source_match.dat", num, matches, zp, NULL);
+#endif
+
+    psVector *trans = pmSourceMatchRelphot(matches, zp, tol, iter1, starRej1, starSys1,
+                                           iter2, starRej2, starSys2, starLimit,
+                                           transIter, transRej, transThresh); // Transparencies for each image
+    if (!trans) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to measure transparencies");
+        return NAN;
+    }
+
+#ifdef TESTING
+    dumpMatches("source_mags.dat", num, matches, zp, trans);
+#endif
+
+    for (int i = 0; i < trans->n; i++) {
+        if (!isfinite(trans->data.F32[i])) {
+            inputMask->data.U8[i] = PPSTACK_MASK_CAL;
+        }
+    }
 
     // Save best matches SOMEWHERE for future photometry
     // XXX this is a really poor output location; clean up the pmFPAfiles used in ppStack
     pmCell *sourcesCell = pmFPAfileThisCell(config->files, view, "PPSTACK.OUTPUT");
-    psArray *sourcesBest = psArrayAllocEmpty (100);
-
-    // XXX something of a hack: require at 2 detections or 1/2 of the max possible
-    int minMatches = PS_MAX (2, 0.5*num);
+    psArray *sourcesBest = psArrayAllocEmpty(matches->n);
+
+    // XXX something of a hack: require at least 2 detections or the nominated fraction of the max possible
+    int minMatches = PS_MAX(2, fracMatch * num);// Minimum number of matches required
     for (int i = 0; i < matches->n; i++) {
-	pmSourceMatch *match = matches->data[i]; // Match of interest
-	if (match->num < minMatches) continue;
-
-	// We need to grab a single instance of this source: just take the first available
-	int nImage = match->image->data.S32[0];
-	int nIndex = match->index->data.S32[0];
-	psArray *sources = sourceLists->data[nImage];
-	pmSource *source = sources->data[nIndex];
-	
-	// stick this sample source on sourcesBest
-	psArrayAdd (sourcesBest, 100, source);
-    }
-    psMetadataAdd (sourcesCell->analysis, PS_LIST_TAIL, "PSPHOT.SOURCES", PS_DATA_ARRAY | PS_META_REPLACE, "psphot sources", sourcesBest);
-    psLogMsg("ppStack", PS_LOG_INFO, "Selected %ld sources for photometry analysis\n", sourcesBest->n);
-    psFree (sourcesBest);
+        pmSourceMatch *match = matches->data[i]; // Match of interest
+        if (match->num < minMatches) {
+            continue;
+        }
+
+        // We need to grab a single instance of this source: just take the first available
+        int image = match->image->data.S32[0]; // Index of image
+        int index = match->index->data.S32[0]; // Index of source within image
+        psArray *sources = sourceLists->data[image]; // Sources for image
+        pmSource *source = sources->data[index]; // Source of interest
+
+        psArrayAdd(sourcesBest, sourcesBest->n, source);
+    }
+    psMetadataAdd(sourcesCell->analysis, PS_LIST_TAIL, "PSPHOT.SOURCES", PS_DATA_ARRAY | PS_META_REPLACE,
+                  "psphot sources", sourcesBest);
+    psLogMsg("ppStack", PS_LOG_INFO, "Selected %ld sources for photometry analysis", sourcesBest->n);
+    psFree(sourcesBest);
 
     psFree(matches);
-    if (!trans) {
-        psError(PS_ERR_UNKNOWN, false, "Unable to measure transparencies");
-        return NAN;
-    }
 
     // M = m + c0 + c1 * airmass - 2.5log(t) + transparency
@@ -182,4 +224,7 @@
     // We don't need to know the magnitude zero point for the filter, since it cancels out
     for (int i = 0; i < num; i++) {
+        if (!isfinite(trans->data.F32[i])) {
+            continue;
+        }
         psArray *sources = sourceLists->data[i]; // Sources of interest
         float magCorr = airmassTerm - 2.5*log10(sumExpTime) - zp->data.F32[i] - trans->data.F32[i];
Index: /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackThread.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackThread.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackThread.c	(revision 23352)
@@ -9,4 +9,7 @@
 
 #include "ppStack.h"
+#include "ppStackOptions.h"
+#include "ppStackThread.h"
+
 
 #define THREAD_WAIT 10000               // Microseconds to wait if thread is not available
@@ -36,7 +39,13 @@
     psFree(stack->threads);
     for (int i = 0; i < stack->imageFits->n; i++) {
-        psFitsClose(stack->imageFits->data[i]);
-        psFitsClose(stack->maskFits->data[i]);
-        psFitsClose(stack->varianceFits->data[i]);
+        if (stack->imageFits->data[i]) {
+            psFitsClose(stack->imageFits->data[i]);
+        }
+        if (stack->maskFits->data[i]) {
+            psFitsClose(stack->maskFits->data[i]);
+        }
+        if (stack->varianceFits->data[i]) {
+            psFitsClose(stack->varianceFits->data[i]);
+        }
         stack->imageFits->data[i] = stack->maskFits->data[i] = stack->varianceFits->data[i] = NULL;
     }
@@ -47,8 +56,15 @@
 }
 
-ppStackThreadData *ppStackThreadDataSetup(const psArray *cells, const psArray *imageNames,
-                                          const psArray *maskNames, const psArray *varianceNames,
-                                          const psArray *covariances, const pmConfig *config)
-{
+ppStackThreadData *ppStackThreadDataSetup(const ppStackOptions *options, const pmConfig *config)
+{
+    psAssert(options, "Require options");
+    psAssert(config, "Require configuration");
+
+    const psArray *cells = options->cells; // Array of input cells
+    const psArray *imageNames = options->imageNames; // Names of images to read
+    const psArray *maskNames = options->maskNames; // Names of masks to read
+    const psArray *varianceNames = options->varianceNames; // Names of variance maps to read
+    const psArray *covariances = options->covariances; // Covariance matrices (already read)
+
     PS_ASSERT_ARRAY_NON_NULL(cells, NULL);
     PS_ASSERT_ARRAYS_SIZE_EQUAL(cells, imageNames, NULL);
@@ -239,5 +255,5 @@
 
     {
-        psThreadTask *task = psThreadTaskAlloc("PPSTACK_INITIAL_COMBINE", 6);
+        psThreadTask *task = psThreadTaskAlloc("PPSTACK_INITIAL_COMBINE", 3);
         task->function = &ppStackReadoutInitialThread;
         psThreadTaskAdd(task);
@@ -253,5 +269,5 @@
 
     {
-        psThreadTask *task = psThreadTaskAlloc("PPSTACK_FINAL_COMBINE", 7);
+        psThreadTask *task = psThreadTaskAlloc("PPSTACK_FINAL_COMBINE", 3);
         task->function = &ppStackReadoutFinalThread;
         psThreadTaskAdd(task);
Index: /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackThread.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackThread.h	(revision 23352)
+++ /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackThread.h	(revision 23352)
@@ -0,0 +1,52 @@
+#ifndef PPSTACK_THREAD_H
+#define PPSTACK_THREAD_H
+
+#include <pslib.h>
+#include <psmodules.h>
+
+#include "ppStackOptions.h"
+
+// Thread for stacking chunks
+//
+// Each input file contributes a readout, into which is read a chunk from that file
+typedef struct {
+    psArray *readouts;                  // Input readouts to read and stack
+    bool read;                          // Has the scan been read?
+    bool busy;                          // Is the scan being processed?
+    int firstScan;                      // First row of the chunk to be read for this group
+    int lastScan;                       // Last row of the chunk to be read for this group
+} ppStackThread;
+
+// Allocator
+ppStackThread *ppStackThreadAlloc(
+    psArray *readouts                   // Inputs readouts to read and stack
+    );
+
+// Data for threads
+typedef struct {
+    psArray *threads;                   // Threads doing stacking
+    int lastScan;                       // Last row that's been read
+    psArray *imageFits;                 // FITS file pointers for images
+    psArray *maskFits;                  // FITS file pointers for masks
+    psArray *varianceFits;              // FITS file pointers for variances
+} ppStackThreadData;
+
+// Set up thread data
+ppStackThreadData *ppStackThreadDataSetup(
+    const ppStackOptions *options,      // Options
+    const pmConfig *config              // Configuration
+    );
+
+// Read chunk into the first available file thread
+ppStackThread *ppStackThreadRead(bool *status, // Status of read
+                                 ppStackThreadData *stack, // Stacks available for reading
+                                 pmConfig *config, // Configuration
+                                 int numChunk, // Chunk number (only for interest)
+                                 int overlap // Overlap between subsequent scans
+    );
+
+// Initialise the threads
+void ppStackThreadInit(void);
+
+
+#endif
Index: /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackVersion.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackVersion.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackVersion.c	(revision 23352)
@@ -7,51 +7,108 @@
 #include <psmodules.h>
 #include <ppStats.h>
+#include <psphot.h>
 
 #include "ppStack.h"
 
-static const char *cvsTag = "$Name: not supported by cvs2svn $";// CVS tag name
+#ifndef PPSTACK_VERSION
+#error "PPSTACK_VERSION is not set"
+#endif
+#ifndef PPSTACK_BRANCH
+#error "PPSTACK_BRANCH is not set"
+#endif
+#ifndef PPSTACK_SOURCE
+#error "PPSTACK_SOURCE is not set"
+#endif
+
+#define xstr(s) str(s)
+#define str(s) #s
 
 psString ppStackVersion(void)
 {
-    psString version = NULL;            // Version, to return
-    psStringAppend(&version, "%s-%s",PACKAGE_NAME,PACKAGE_VERSION);
-    return version;
+    char *value = NULL;
+    psStringAppend(&value, "%s@%s", xstr(PPSTACK_BRANCH), xstr(PPSTACK_VERSION));
+    return value;
+}
+
+psString ppStackSource(void)
+{
+    return psStringCopy(xstr(PPSTACK_SOURCE));
 }
 
 psString ppStackVersionLong(void)
 {
-    psString version = ppStackVersion(); // Version, to return
-    psString tag = psStringStripCVS(cvsTag, "Name"); // CVS tag
-    psStringAppend(&version, " (cvs tag %s) %s, %s", tag, __DATE__, __TIME__);
-    psFree(tag);
+    psString version = ppStackVersion();  // Version, to return
+    psString source = ppStackSource();    // Source
+
+    psStringPrepend(&version, "ppStack ");
+    psStringAppend(&version, " from %s, built %s, %s", source, __DATE__, __TIME__);
+    psFree(source);
+
+#ifdef __OPTIMIZE__
+    psStringAppend(&version, " optimised");
+#else
+    psStringAppend(&version, " unoptimised");
+#endif
+
     return version;
-}
+};
 
 
-void ppStackVersionMetadata(psMetadata *metadata)
+bool ppStackVersionHeader(psMetadata *header)
 {
-    PS_ASSERT_METADATA_NON_NULL(metadata,);
-
-    psString pslib = psLibVersionLong();// psLib version
-    psString psmodules = psModulesVersionLong(); // psModules version
-    psString ppStats = ppStatsVersionLong(); // ppStats version
-    psString ppStack = ppStackVersionLong(); // ppStack version
+    PS_ASSERT_METADATA_NON_NULL(header, false);
 
     psTime *time = psTimeGetNow(PS_TIME_TAI); // The time now
     psString timeString = psTimeToISO(time); // The time in an ISO string
     psFree(time);
-    psString head = NULL;               // Head string
-    psStringAppend(&head, "ppStack processing at %s. Component information:", timeString);
+    psString history = NULL;               // History string
+    psStringAppend(&history, "ppStack at %s", timeString);
+    psFree(timeString);
+    psMetadataAddStr(header, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, NULL, history);
+    psFree(history);
+
+    psLibVersionHeader(header);
+    psModulesVersionHeader(header);
+    psphotVersionHeader(header);
+    ppStatsVersionHeader(header);
+
+    psString version = ppStackVersion(); // Software version
+    psString source  = ppStackSource();  // Software source
+
+    psStringPrepend(&version, "ppStack version: ");
+    psStringPrepend(&source, "ppStack source: ");
+
+    psMetadataAddStr(header, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, NULL, version);
+    psMetadataAddStr(header, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, NULL, source);
+
+    psFree(version);
+    psFree(source);
+
+    return true;
+}
+
+void ppStackVersionPrint(void)
+{
+    psTime *time = psTimeGetNow(PS_TIME_TAI); // The time now
+    psString timeString = psTimeToISO(time); // The time in an ISO string
+    psFree(time);
+    psLogMsg("ppStack", PS_LOG_INFO, "ppStack at %s", timeString);
     psFree(timeString);
 
-    psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, "", head);
-    psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, "", pslib);
-    psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, "", psmodules);
-    psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, "", ppStats);
-    psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, "", ppStack);
+    psString pslib = psLibVersionLong();// psLib version
+    psString psmodules = psModulesVersionLong(); // psModules version
+    psString psphot = psphotVersionLong(); // psphot version
+    psString ppStats = ppStatsVersionLong(); // psastro version
+    psString ppStack = ppStackVersionLong(); // ppStack version
 
-    psFree(head);
+    psLogMsg("ppStack", PS_LOG_INFO, "%s", pslib);
+    psLogMsg("ppStack", PS_LOG_INFO, "%s", psmodules);
+    psLogMsg("ppStack", PS_LOG_INFO, "%s", psphot);
+    psLogMsg("ppStack", PS_LOG_INFO, "%s", ppStats);
+    psLogMsg("ppStack", PS_LOG_INFO, "%s", ppStack);
+
     psFree(pslib);
     psFree(psmodules);
+    psFree(psphot);
     psFree(ppStats);
     psFree(ppStack);
Index: /branches/cnb_branches/cnb_branch_20090301/ppStats/src/Makefile.am
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppStats/src/Makefile.am	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ppStats/src/Makefile.am	(revision 23352)
@@ -1,4 +1,14 @@
 lib_LTLIBRARIES = libppStats.la
-libppStats_la_CFLAGS = $(PPSTATS_CFLAGS) $(PSMODULE_CFLAGS) $(PSLIB_CFLAGS)
+
+# PPSTATS_VERSION=`if [ -e ../../VERSION ]; then cat ../../VERSION; else svnversion; fi`
+# PPSTATS_BRANCH=`if [ -e ../../BRANCH ]; then cat ../../BRANCH; else svn info | sed -n -e '/URL:/ h' -e '/Repository Root:/ { x; H; x; s|Repository Root: \(.*\)\nURL: \1\(.*\)|\2| ; s|^/|| ; s|/[a-zA-Z]*/src.*|| ; p }'; fi`
+# PPSTATS_SOURCE=`if [ -e ../../SOURCE ]; then cat ../../SOURCE; else svn info | sed -n -e 's/Repository UUID: // p'; fi`
+# 
+# # Force recompilation of ppStatsVersion.c, since it gets the version information
+# ppStatsVersion.c: FORCE
+# 	touch ppStatsVersion.c
+# FORCE: ;
+
+libppStats_la_CFLAGS = $(PPSTATS_CFLAGS) $(PSMODULE_CFLAGS) $(PSLIB_CFLAGS) -DPPSTATS_VERSION=$(SVN_VERSION) -DPPSTATS_BRANCH=$(SVN_BRANCH) -DPPSTATS_SOURCE=$(SVN_SOURCE)
 libppStats_la_LDFLAGS = $(PPSTATS_LIBS) $(PSMODULE_LIBS) $(PSLIB_LIBS)
 
Index: /branches/cnb_branches/cnb_branch_20090301/ppStats/src/ppStats.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppStats/src/ppStats.h	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ppStats/src/ppStats.h	(revision 23352)
@@ -22,7 +22,7 @@
     psList *summary;                    // Summary statistics to calculate
     // Options for input data
-    bool doFirstReadout3D;		// for 3D data, use the first readout?
+    bool doFirstReadout3D;              // for 3D data, use the first readout?
     float sample;                       // Fraction of cell to sample for statistics
-    psImageMaskType maskVal;		// Mask value for images
+    psImageMaskType maskVal;            // Mask value for images
     psList *chips;                      // Chips to look at
     psList *cells;                      // Cells to look at
@@ -108,6 +108,15 @@
 psString ppStatsVersion(void);
 
+/// Return source information
+psString ppStatsSource(void);
+
 /// Return long version information
 psString ppStatsVersionLong(void);
+
+/// Populate header with version information
+bool ppStatsVersionHeader(
+    psMetadata *header                  ///< Header to populate
+    );
+
 
 void p_ppStatsGetMetadata(psMetadata *target, // Target for metadata
Index: /branches/cnb_branches/cnb_branch_20090301/ppStats/src/ppStatsFromMetadataPrint.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppStats/src/ppStatsFromMetadataPrint.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ppStats/src/ppStatsFromMetadataPrint.c	(revision 23352)
@@ -41,5 +41,7 @@
             VALUE_NUMERICAL_CASE(F64, "lf",   F64);
           case PS_DATA_STRING:
-            fprintf(f, "%s '%s' ", entry->flag, entry->value->data.str);
+            if (entry->value->data.str) {
+                fprintf(f, "%s '%s' ", entry->flag, entry->value->data.str);
+            }
             break;
           case PS_DATA_BOOL:
@@ -50,7 +52,9 @@
           case PS_DATA_TIME: {
               psTime *t = (psTime*)entry->value->data.V;
-              psString str = psTimeToISO(t);
-              fprintf(f, "%s %.19sZ ", entry->flag, str);
-              psFree(str);
+              if (t) {
+                  psString str = psTimeToISO(t);
+                  fprintf(f, "%s %.19sZ ", entry->flag, str);
+                  psFree(str);
+              }
               break;
           }
Index: /branches/cnb_branches/cnb_branch_20090301/ppStats/src/ppStatsVersion.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppStats/src/ppStatsVersion.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ppStats/src/ppStatsVersion.c	(revision 23352)
@@ -1,19 +1,63 @@
 #include "ppStatsInternal.h"
 
-static const char *cvsTag = "$Name: not supported by cvs2svn $";// CVS tag name
+#ifndef PPSTATS_VERSION
+#error "PPSTATS_VERSION is not set"
+#endif
+#ifndef PPSTATS_BRANCH
+#error "PPSTATS_BRANCH is not set"
+#endif
+#ifndef PPSTATS_SOURCE
+#error "PPSTATS_SOURCE is not set"
+#endif
+
+#define xstr(s) str(s)
+#define str(s) #s
 
 psString ppStatsVersion(void)
 {
-    psString version = NULL;            // Version, to return
-    psStringAppend(&version, "%s-%s",PACKAGE_NAME,PACKAGE_VERSION);
-    return version;
+    char *value = NULL;
+    psStringAppend(&value, "%s@%s", xstr(PPSTATS_BRANCH), xstr(PPSTATS_VERSION));
+    return value;
+}
+
+psString ppStatsSource(void)
+{
+    return psStringCopy(xstr(PPSTATS_SOURCE));
 }
 
 psString ppStatsVersionLong(void)
 {
-    psString version = ppStatsVersion(); // Version, to return
-    psString tag = psStringStripCVS(cvsTag, "Name"); // CVS tag
-    psStringAppend(&version, " (cvs tag %s) %s, %s", tag, __DATE__, __TIME__);
-    psFree(tag);
+    psString version = ppStatsVersion();  // Version, to return
+    psString source = ppStatsSource();    // Source
+
+    psStringPrepend(&version, "ppStats ");
+    psStringAppend(&version, " from %s, built %s, %s", source, __DATE__, __TIME__);
+    psFree(source);
+
+#ifdef __OPTIMIZE__
+    psStringAppend(&version, " optimised");
+#else
+    psStringAppend(&version, " unoptimised");
+#endif
+
     return version;
+};
+
+bool ppStatsVersionHeader(psMetadata *header)
+{
+    PS_ASSERT_METADATA_NON_NULL(header, false);
+
+    psString version = ppStatsVersion(); // Software version
+    psString source = ppStatsSource();   // Software source
+
+    psStringPrepend(&version, "ppStats version: ");
+    psStringPrepend(&source, "ppStats source: ");
+
+    psMetadataAddStr(header, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, NULL, version);
+    psMetadataAddStr(header, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, NULL, source);
+
+    psFree(version);
+    psFree(source);
+
+    return true;
 }
Index: /branches/cnb_branches/cnb_branch_20090301/ppSub/src/Makefile.am
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppSub/src/Makefile.am	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ppSub/src/Makefile.am	(revision 23352)
@@ -1,5 +1,14 @@
 bin_PROGRAMS = ppSub ppSubKernel
 
-ppSub_CPPFLAGS = $(PSLIB_CFLAGS) $(PSMODULE_CFLAGS) $(PPSTATS_CFLAGS) $(PSPHOT_CFLAGS) $(PPSUB_CFLAGS)
+# PPSUB_VERSION=`if [ -e ../../VERSION ]; then cat ../../VERSION; else svnversion; fi`
+# PPSUB_BRANCH=`if [ -e ../../BRANCH ]; then cat ../../BRANCH; else svn info | sed -n -e '/URL:/ h' -e '/Repository Root:/ { x; H; x; s|Repository Root: \(.*\)\nURL: \1\(.*\)|\2| ; s|^/|| ; s|/[a-zA-Z]*/src.*|| ; p }'; fi`
+# PPSUB_SOURCE=`if [ -e ../../SOURCE ]; then cat ../../SOURCE; else svn info | sed -n -e 's/Repository UUID: // p'; fi`
+# 
+# # Force recompilation of ppSubVersion.c, since it gets the version information
+# ppSubVersion.c: FORCE
+# 	touch ppSubVersion.c
+# FORCE: ;
+
+ppSub_CPPFLAGS = $(PSLIB_CFLAGS) $(PSMODULE_CFLAGS) $(PPSTATS_CFLAGS) $(PSPHOT_CFLAGS) $(PPSUB_CFLAGS) -DPPSUB_VERSION=$(SVN_VERSION) -DPPSUB_BRANCH=$(SVN_BRANCH) -DPPSUB_SOURCE=$(SVN_SOURCE)
 ppSub_LDFLAGS  = $(PSLIB_LIBS)   $(PSMODULE_LIBS)   $(PPSTATS_LIBS)   $(PSPHOT_LIBS)   $(PPSUB_LIBS)
 
Index: /branches/cnb_branches/cnb_branch_20090301/ppSub/src/ppSub.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppSub/src/ppSub.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ppSub/src/ppSub.c	(revision 23352)
@@ -34,4 +34,6 @@
         goto die;
     }
+
+    ppSubVersionPrint();
 
     if (!pmModelClassInit()) {
@@ -75,5 +77,5 @@
     psTimerStop();
 
-    pmSubtractionVisualClose(); //close plot windows, if -visual is set
+    pmVisualClose(); //close plot windows, if -visual is set
     psFree(config);
     pmModelClassCleanup();
Index: /branches/cnb_branches/cnb_branch_20090301/ppSub/src/ppSub.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppSub/src/ppSub.h	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ppSub/src/ppSub.h	(revision 23352)
@@ -89,7 +89,10 @@
     );
 
-/// Put the program version information into a metadata
-void ppSubVersionMetadata(psMetadata *metadata ///< Metadata to populate
+/// Put the program version information into a header
+bool ppSubVersionHeader(psMetadata *header ///< Header to populate
     );
+
+/// Print version information
+void ppSubVersionPrint(void);
 
 
Index: /branches/cnb_branches/cnb_branch_20090301/ppSub/src/ppSubArguments.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppSub/src/ppSubArguments.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ppSub/src/ppSubArguments.c	(revision 23352)
@@ -29,6 +29,6 @@
 {
     fprintf(stderr, "\nPan-STARRS PSF-matched image subtraction\n\n");
-    fprintf(stderr, "Usage: %s INPUT.fits REFERENCE.fits OUTPUT_ROOT \n"
-            "\t[-psf REFERENCE.psf.fits] [-sources REFERENCE.cmf]\n\n"
+    fprintf(stderr, "Usage: %s OUTPUT_ROOT \n"
+            "\t[-psf REFERENCE.psf.fits]\n\n"
             "This subtracts the convolved REFERENCE from the INPUT, by default.\n",
             program);
@@ -191,5 +191,5 @@
 bool ppSubArgumentsSetup(int argc, char *argv[], pmConfig *config)
 {
-    int argnum;                         // Argument Number
+    //    int argnum;                         // Argument Number
     assert(config);
 
@@ -200,16 +200,15 @@
     }
 
-    if ((argnum = psArgumentGet(argc, argv, "-psphot-visual"))) {
-        psArgumentRemove(argnum, &argc, argv);
-        psphotSetVisual(true);
-    }
-
-    pmConfigFileSetsMD(config->arguments, &argc, argv, "PPSUB.SOURCES", "-sources", NULL);
 
     psMetadata *arguments = config->arguments; // Command-line arguments
+    psMetadataAddStr(arguments, PS_LIST_TAIL, "-inimage", 0, "Input image", NULL);
     psMetadataAddStr(arguments, PS_LIST_TAIL, "-inmask", 0, "Input mask image", NULL);
     psMetadataAddStr(arguments, PS_LIST_TAIL, "-invariance", 0, "Input variance image", NULL);
+    psMetadataAddStr(arguments, PS_LIST_TAIL, "-insources", 0, "Input source list", NULL);
+    psMetadataAddStr(arguments, PS_LIST_TAIL, "-refimage", 0, "Reference image", NULL);
     psMetadataAddStr(arguments, PS_LIST_TAIL, "-refmask", 0, "Reference mask image", NULL);
     psMetadataAddStr(arguments, PS_LIST_TAIL, "-refvariance", 0, "Reference variance image", NULL);
+    psMetadataAddStr(arguments, PS_LIST_TAIL, "-refsources", 0, "Reference source list", NULL);
+    psMetadataAddStr(arguments, PS_LIST_TAIL, "-kernel", 0, "Precalculated kernel to apply", NULL);
     psMetadataAddStr(arguments, PS_LIST_TAIL, "-stats", 0, "Statistics file", NULL);
     psMetadataAddF32(arguments, PS_LIST_TAIL, "-region", 0, "Size of iso-kernel region", NAN);
@@ -228,4 +227,5 @@
     psMetadataAddF32(arguments, PS_LIST_TAIL, "-spacing", 0, "Typical stamp spacing (pixels)", NAN);
     psMetadataAddS32(arguments, PS_LIST_TAIL, "-footprint", 0, "Stamp footprint half-size (pixels)", -1);
+    psMetadataAddF32(arguments, PS_LIST_TAIL, "-source-radius", 0, "Source matching radius (pixels)", NAN);
     psMetadataAddS32(arguments, PS_LIST_TAIL, "-stride", 0, "Size of convolution patches (pixels)", -1);
     psMetadataAddF32(arguments, PS_LIST_TAIL, "-threshold", 0, "Minimum threshold for stamps (ADU)", NAN);
@@ -256,12 +256,15 @@
     psMetadataAddBool(arguments, PS_LIST_TAIL, "-visual", 0, "Show diagnostic plots", NULL);
 
-    if (argc == 1 || !psArgumentParse(arguments, &argc, argv) || argc != 4) {
+    if (argc == 1 || !psArgumentParse(arguments, &argc, argv) || argc != 2) {
         usage(argv[0], arguments, config);
     }
 
-    fileList("INPUT", argv[1], "Name of the input image",     config);
-    fileList("REF",   argv[2], "Name of the reference image", config);
     psMetadataAddStr(config->arguments, PS_LIST_TAIL, "OUTPUT", 0, "Name of the output image", argv[3]);
 
+
+    const char *inImage = psMetadataLookupStr(NULL, arguments, "-inimage"); // Name of input image
+    if (inImage && strlen(inImage) > 0) {
+        fileList("INPUT", inImage, "Name of the input image", config);
+    }
     const char *inMask = psMetadataLookupStr(NULL, arguments, "-inmask"); // Name of input mask
     if (inMask && strlen(inMask) > 0) {
@@ -272,5 +275,13 @@
         fileList("INPUT.VARIANCE", inVariance, "Name of the input variance image", config);
     }
-
+    const char *inSources = psMetadataLookupStr(NULL, arguments, "-insources"); // Name of input source list
+    if (inSources && strlen(inSources) > 0) {
+        fileList("INPUT.SOURCES", inSources, "Name of the input source list", config);
+    }
+
+    const char *refImage = psMetadataLookupStr(NULL, arguments, "-refimage"); // Name of reference image
+    if (refImage && strlen(refImage) > 0) {
+        fileList("INPUT", refImage, "Name of the reference image", config);
+    }
     const char *refMask = psMetadataLookupStr(NULL, arguments, "-refmask"); // Name of reference mask
     if (refMask && strlen(refMask) > 0) {
@@ -280,4 +291,13 @@
     if (refVariance && strlen(refVariance) > 0) {
         fileList("REF.VARIANCE", refVariance, "Name of the reference variance image", config);
+    }
+    const char *refSources = psMetadataLookupStr(NULL, arguments, "-refsources"); // Name of ref source list
+    if (refSources && strlen(refSources) > 0) {
+        fileList("REF.SOURCES", refSources, "Name of the reference source list", config);
+    }
+
+    const char *kernel = psMetadataLookupStr(NULL, arguments, "-kernel"); // Name of kernel
+    if (kernel && strlen(kernel) > 0) {
+        fileList("KERNEL", kernel, "Name of the kernel to apply", config);
     }
 
@@ -304,22 +324,23 @@
     }
 
-    VALUE_ARG_RECIPE_FLOAT("-region",     "REGION.SIZE",     F32);
-    VALUE_ARG_RECIPE_INT("-size",         "KERNEL.SIZE",     S32, 0);
-    VALUE_ARG_RECIPE_INT("-order",        "SPATIAL.ORDER",   S32, -1);
-    VALUE_ARG_RECIPE_FLOAT("-spacing",    "STAMP.SPACING",   F32);
-    VALUE_ARG_RECIPE_INT("-rings-order",  "RINGS.ORDER",     S32, -1);
-    VALUE_ARG_RECIPE_INT("-inner",        "INNER",           S32, -1);
-    VALUE_ARG_RECIPE_INT("-spam-binning", "SPAM.BINNING",    S32, 0);
-    VALUE_ARG_RECIPE_INT("-footprint",    "STAMP.FOOTPRINT", S32, -1);
-    VALUE_ARG_RECIPE_INT("-stride",       "STRIDE",          S32, -1);
-    VALUE_ARG_RECIPE_FLOAT("-threshold",  "STAMP.THRESHOLD", F32);
-    VALUE_ARG_RECIPE_INT("-iter",         "ITER",            S32, -1);
-    VALUE_ARG_RECIPE_FLOAT("-rej",        "REJ",             F32);
-    VALUE_ARG_RECIPE_FLOAT("-sys",        "SYS",             F32);
-    VALUE_ARG_RECIPE_FLOAT("-badfrac",    "BADFRAC",         F32);
-    VALUE_ARG_RECIPE_FLOAT("-penalty",    "PENALTY",         F32);
-    VALUE_ARG_RECIPE_FLOAT("-poor-frac",  "POOR.FRACTION",   F32);
-    VALUE_ARG_RECIPE_INT("-bin1",         "BIN1",            S32, 0);
-    VALUE_ARG_RECIPE_INT("-bin2",         "BIN2",            S32, 0);
+    VALUE_ARG_RECIPE_FLOAT("-region",        "REGION.SIZE",     F32);
+    VALUE_ARG_RECIPE_INT("-size",            "KERNEL.SIZE",     S32, 0);
+    VALUE_ARG_RECIPE_INT("-order",           "SPATIAL.ORDER",   S32, -1);
+    VALUE_ARG_RECIPE_FLOAT("-spacing",       "STAMP.SPACING",   F32);
+    VALUE_ARG_RECIPE_INT("-rings-order",     "RINGS.ORDER",     S32, -1);
+    VALUE_ARG_RECIPE_INT("-inner",           "INNER",           S32, -1);
+    VALUE_ARG_RECIPE_INT("-spam-binning",    "SPAM.BINNING",    S32, 0);
+    VALUE_ARG_RECIPE_INT("-footprint",       "STAMP.FOOTPRINT", S32, -1);
+    VALUE_ARG_RECIPE_FLOAT("-source-radius", "SOURCE.RADIUS",   F32);
+    VALUE_ARG_RECIPE_INT("-stride",          "STRIDE",          S32, -1);
+    VALUE_ARG_RECIPE_FLOAT("-threshold",     "STAMP.THRESHOLD", F32);
+    VALUE_ARG_RECIPE_INT("-iter",            "ITER",            S32, -1);
+    VALUE_ARG_RECIPE_FLOAT("-rej",           "REJ",             F32);
+    VALUE_ARG_RECIPE_FLOAT("-sys",           "SYS",             F32);
+    VALUE_ARG_RECIPE_FLOAT("-badfrac",       "BADFRAC",         F32);
+    VALUE_ARG_RECIPE_FLOAT("-penalty",       "PENALTY",         F32);
+    VALUE_ARG_RECIPE_FLOAT("-poor-frac",     "POOR.FRACTION",   F32);
+    VALUE_ARG_RECIPE_INT("-bin1",            "BIN1",            S32, 0);
+    VALUE_ARG_RECIPE_INT("-bin2",            "BIN2",            S32, 0);
 
     valueArgRecipeStr(arguments, recipe, "-mask-in",   "MASK.IN",  recipe);
@@ -360,5 +381,5 @@
 
     if (psMetadataLookupBool(NULL, arguments, "-visual")) {
-        pmSubtractionSetVisual(true);
+        pmVisualSetVisual(true);
     }
 
@@ -376,15 +397,4 @@
     psTrace("ppSub", 1, "Done reading command-line arguments\n");
 
-    // Dump configuration, now that's it's settled
-    psBool status;
-    psString dump_file =  psMetadataLookupStr(&status, config->arguments, "-dumpconfig");
-    if (dump_file) {
-        pmConfigCamerasCull(config, NULL);
-        pmConfigRecipesCull(config, "PPSUB,PPSTATS,PSPHOT,MASKS");
-
-        pmFPAfile *input = psMetadataLookupPtr(NULL, config->files, "PPSUB.INPUT"); // Input file
-        pmConfigDump(config, input->fpa, dump_file);
-    }
-
     // XXX move this to ppSubArguments
     int threads = psMetadataLookupS32(NULL, config->arguments, "-threads"); // Number of threads
Index: /branches/cnb_branches/cnb_branch_20090301/ppSub/src/ppSubBackground.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppSub/src/ppSubBackground.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ppSub/src/ppSubBackground.c	(revision 23352)
@@ -53,5 +53,5 @@
         }
     }
-    psImageBinning *binning = psMetadataLookupPtr(&mdok, psphotRecipe,
+    psImageBinning *binning = psMetadataLookupPtr(&mdok, modelRO->analysis,
                                                   "PSPHOT.BACKGROUND.BINNING"); // Binning for model
     psImage *modelImage = modelRO->image; // Background model
Index: /branches/cnb_branches/cnb_branch_20090301/ppSub/src/ppSubCamera.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppSub/src/ppSubCamera.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ppSub/src/ppSubCamera.c	(revision 23352)
@@ -22,188 +22,193 @@
 #include "ppSub.h"
 
+// Define an input file
+static pmFPAfile *defineInputFile(pmConfig *config,// Configuration
+                                  pmFPAfile *bind,    // File to which to bind, or NULL
+                                  char *filerule,     // Name of file rule
+                                  char *argname,      // Argument name
+                                  pmFPAfileType fileType // Type of file
+    )
+{
+    bool status;
+
+    // look for the file on the RUN metadata
+    pmFPAfile *file = pmFPAfileDefineFromRun(&status, config, filerule); // File to return
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, false, "Failed to load file definition for %s", filerule);
+        return NULL;
+    }
+    if (!file) {
+        // look for the file on the argument list
+        if (bind) {
+            file = pmFPAfileBindFromArgs(&status, bind, config, filerule, argname);
+        } else {
+            file = pmFPAfileDefineFromArgs(&status, config, filerule, argname);
+        }
+        if (!status) {
+            psError(PS_ERR_UNKNOWN, false, "Failed to load file definition for %s", filerule);
+            return false;
+        }
+    }
+
+    if (!file) {
+        return NULL;
+    }
+
+    if (file->type != fileType) {
+        psError(PS_ERR_UNKNOWN, true, "%s is not of type %s", filerule, pmFPAfileStringFromType(fileType));
+        return NULL;
+    }
+
+    return file;
+}
+
+// Define an output file
+static pmFPAfile *defineOutputFile(pmConfig *config, // Configuration
+                                   pmFPAfile *template,    // File to use as basis for definition
+                                   char *filerule,     // Name of file rule
+                                   pmFPAfileType fileType // Type of file
+    )
+{
+
+    pmFPAfile *file = pmFPAfileDefineFromFile(config, template, 1, 1, filerule);
+    if (!file) {
+        psError(PS_ERR_IO, false, _("Unable to generate output file from %s"), filerule);
+        return NULL;
+    }
+    if (file->type != fileType) {
+        psError(PS_ERR_IO, true, "%s is not of type %s", filerule, pmFPAfileStringFromType(fileType));
+        return NULL;
+    }
+
+    return file;
+}
+
+
+// Define an output file that will be used in a calculation
+// This means it might already be available in the RUN metadata
+static pmFPAfile *defineCalcFile(pmConfig *config, // Configuration
+                                 pmFPAfile *bind,    // File to which to bind, or NULL
+                                 char *filerule,     // Name of file rule
+                                 pmFPAfileType fileType // Type of file
+    )
+{
+    bool status;
+
+    // look for the file on the RUN metadata
+    pmFPAfile *file = pmFPAfileDefineFromRun(&status, config, filerule); // File to return
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, false, "Failed to load file definition for %s", filerule);
+        return NULL;
+    }
+    if (file) {
+        // It's an input
+        file->save = false;
+    }
+
+    // define new version of file
+    if (!file) {
+        pmFPAfileDefineOutput(config, bind ? bind->fpa : NULL, filerule);
+        if (!status) {
+            psError(PS_ERR_UNKNOWN, false, "Failed to load file definition for %s", filerule);
+            return false;
+        }
+        if (file) {
+            // It's an output
+            file->save = true;
+        }
+    }
+
+    if (!file) {
+        return NULL;
+    }
+
+    if (file->type != fileType) {
+        psError(PS_ERR_UNKNOWN, true, "%s is not of type %s", filerule, pmFPAfileStringFromType(fileType));
+        return NULL;
+    }
+
+    return file;
+}
+
+
 bool ppSubCamera(pmConfig *config)
 {
-    bool status = false;                // result from pmFPAfileDefine operations
+    psAssert(config, "Require configuration");
 
     // Input image
-    pmFPAfile *input = pmFPAfileDefineFromArgs(&status, config, "PPSUB.INPUT", "INPUT");
-    if (!status || !input) {
+    pmFPAfile *input = defineInputFile(config, NULL, "PPSUB.INPUT", "INPUT", PM_FPA_FILE_IMAGE);
+    if (!input) {
         psError(PS_ERR_IO, false, "Failed to build FPA from PPSUB.INPUT");
         return false;
     }
-    if (input->type != PM_FPA_FILE_IMAGE) {
-        psError(PS_ERR_IO, true, "PPSUB.INPUT is not of type IMAGE");
-        return false;
-    }
-
-    // Input mask
-    pmFPAfile *inputMask = pmFPAfileBindFromArgs(&status, input, config, "PPSUB.INPUT.MASK", "INPUT.MASK");
-    if (!status) {
-        psError (PS_ERR_UNKNOWN, false, "Failed to load file definition PPSUB.INPUT.MASK");
-        return NULL;
-    }
-    if (inputMask && inputMask->type != PM_FPA_FILE_MASK) {
-        psError(PS_ERR_IO, true, "PPSUB.INPUT.MASK is not of type MASK");
-        return false;
-    }
-
-    // Input variance map
-    pmFPAfile *inputVariance = pmFPAfileBindFromArgs(&status, input, config, "PPSUB.INPUT.VARIANCE", "INPUT.VARIANCE");
-    if (!status) {
-        psError (PS_ERR_UNKNOWN, false, "Failed to load file definition PPSUB.INPUT.VARIANCE");
-        return NULL;
-    }
-    if (inputVariance && inputVariance->type != PM_FPA_FILE_VARIANCE) {
-        psError(PS_ERR_IO, true, "PPSUB.INPUT.VARIANCE is not of type VARIANCE");
-        return false;
-    }
+    defineInputFile(config, input, "PPSUB.INPUT.MASK", "INPUT.MASK", PM_FPA_FILE_MASK);
+    pmFPAfile *inVar = defineInputFile(config, input, "PPSUB.INPUT.VARIANCE", "INPUT.VARIANCE",
+                                       PM_FPA_FILE_VARIANCE);
+    defineInputFile(config, input, "PPSUB.INPUT.SOURCES", "INPUT.SOURCES", PM_FPA_FILE_CMF);
 
     // Reference image
-    status = false;
-    pmFPAfile *ref = pmFPAfileDefineFromArgs(&status, config, "PPSUB.REF", "REF");
+    pmFPAfile *ref = defineInputFile(config, NULL, "PPSUB.REF", "REF", PM_FPA_FILE_IMAGE);
     if (!ref) {
         psError(PS_ERR_IO, false, "Failed to build FPA from PPSUB.REF");
         return false;
     }
-    if (ref->type != PM_FPA_FILE_IMAGE) {
-        psError(PS_ERR_IO, true, "PPSUB.REF is not of type IMAGE");
-        return false;
-    }
-
-    // Reference mask
-    pmFPAfile *refMask = pmFPAfileBindFromArgs(&status, ref, config, "PPSUB.REF.MASK", "REF.MASK");
-    if (!status) {
-        psError (PS_ERR_UNKNOWN, false, "Failed to load file definition PPSUB.REF.MASK");
-        return NULL;
-    }
-    if (refMask && refMask->type != PM_FPA_FILE_MASK) {
-        psError(PS_ERR_IO, true, "PPSUB.REF.MASK is not of type MASK");
-        return false;
-    }
-
-    // Reference variance map
-    pmFPAfile *refVariance = pmFPAfileBindFromArgs(&status, ref, config, "PPSUB.REF.VARIANCE", "REF.VARIANCE");
-    if (!status) {
-        psError (PS_ERR_UNKNOWN, false, "Failed to load file definition PPSUB.REF.VARIANCE");
-        return NULL;
-    }
-    if (refVariance && refVariance->type != PM_FPA_FILE_VARIANCE) {
-        psError(PS_ERR_IO, true, "PPSUB.REF.VARIANCE is not of type VARIANCE");
-        return false;
-    }
+    defineInputFile(config, ref, "PPSUB.REF.MASK", "REF.MASK", PM_FPA_FILE_MASK);
+    pmFPAfile *refVar = defineInputFile(config, ref, "PPSUB.REF.VARIANCE", "REF.VARIANCE",
+                                        PM_FPA_FILE_VARIANCE);
+    defineInputFile(config, ref, "PPSUB.REF.SOURCES", "REF.SOURCES", PM_FPA_FILE_CMF);
+
 
     // Output image
-    pmFPAfile *output = pmFPAfileDefineFromFile(config, input, 1, 1, "PPSUB.OUTPUT");
-    if (!output) {
-        psError(PS_ERR_IO, false, _("Unable to generate output file from PPSUB.OUTPUT"));
-        return false;
-    }
-    if (output->type != PM_FPA_FILE_IMAGE) {
-        psError(PS_ERR_IO, true, "PPSUB.OUTPUT is not of type IMAGE");
+    pmFPAfile *output = defineOutputFile(config, input, "PPSUB.OUTPUT", PM_FPA_FILE_IMAGE);
+    pmFPAfile *outMask = defineOutputFile(config, output, "PPSUB.OUTPUT.MASK", PM_FPA_FILE_MASK);
+    if (!output || !outMask) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to define output files");
         return false;
     }
     output->save = true;
-
-    // Output mask
-    pmFPAfile *outMask = pmFPAfileDefineOutput(config, output->fpa, "PPSUB.OUTPUT.MASK");
-    if (!outMask) {
-        psError(PS_ERR_IO, false, _("Unable to generate output file from PPSUB.OUTPUT.MASK"));
-        return false;
-    }
-    if (outMask->type != PM_FPA_FILE_MASK) {
-        psError(PS_ERR_IO, true, "PPSUB.OUTPUT.MASK is not of type MASK");
-        return false;
-    }
     outMask->save = true;
-
-    // Output variance
-    if (inputVariance && refVariance) {
-        pmFPAfile *outVariance = pmFPAfileDefineOutput(config, output->fpa, "PPSUB.OUTPUT.VARIANCE");
-        if (!outVariance) {
-            psError(PS_ERR_IO, false, _("Unable to generate output file from PPSUB.OUTPUT.VARIANCE"));
-            return false;
-        }
-        if (outVariance->type != PM_FPA_FILE_VARIANCE) {
-            psError(PS_ERR_IO, true, "PPSUB.OUTPUT.VARIANCE is not of type VARIANCE");
-            return false;
-        }
-        outVariance->save = true;
-    }
+    pmFPAfile *outVar = NULL;
+    if (inVar && refVar) {
+        outVar = defineOutputFile(config, output, "PPSUB.OUTPUT.VARIANCE", PM_FPA_FILE_VARIANCE);
+        if (!outVar) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to define output files");
+            return false;
+        }
+        outVar->save = true;
+    }
+
 
     // Convolved input image
-    pmFPAfile *inConv = pmFPAfileDefineFromFile(config, input, 1, 1, "PPSUB.INPUT.CONV");
-    if (!inConv) {
-        psError(PS_ERR_IO, false, _("Unable to generate output file for PPSUB.INPUT.CONV"));
-        return false;
-    }
-    if (output->type != PM_FPA_FILE_IMAGE) {
-        psError(PS_ERR_IO, true, "PPSUB.INPUT.CONV is not of type IMAGE");
-        return false;
-    }
-    // XXX should be based on recipe : inConv->save = true;
-
-    // Convolved input mask
-    pmFPAfile *inConvMask = pmFPAfileDefineOutput(config, inConv->fpa, "PPSUB.INPUT.CONV.MASK");
-    if (!inConvMask) {
-        psError(PS_ERR_IO, false, _("Unable to generate output file from PPSUB.INPUT.CONV.MASK"));
-        return false;
-    }
-    if (inConvMask->type != PM_FPA_FILE_MASK) {
-        psError(PS_ERR_IO, true, "PPSUB.INPUT.CONV.MASK is not of type MASK");
-        return false;
-    }
-    // XXX should be based on recipe : inConvMask->save = true;
-
-    // Convolved input variance
-    if (inputVariance) {
-        pmFPAfile *inConvVariance = pmFPAfileDefineOutput(config, inConv->fpa, "PPSUB.INPUT.CONV.VARIANCE");
-        if (!inConvVariance) {
-            psError(PS_ERR_IO, false, _("Unable to generate output file from PPSUB.OUTPUT.VARIANCE"));
-            return false;
-        }
-        if (inConvVariance->type != PM_FPA_FILE_VARIANCE) {
-            psError(PS_ERR_IO, true, "PPSUB.INPUT.CONV.VARIANCE is not of type VARIANCE");
-            return false;
-        }
-        // XXX should be based on recipe : inConvVariance->save = true;
+    pmFPAfile *inConvImage = defineOutputFile(config, input, "PPSUB.INPUT.CONV", PM_FPA_FILE_IMAGE);
+    pmFPAfile *inConvMask = defineOutputFile(config, input, "PPSUB.INPUT.CONV.MASK", PM_FPA_FILE_MASK);
+    if (!inConvImage || !inConvMask) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to define output files");
+        return false;
+    }
+    if (inVar) {
+        pmFPAfile *inConvVar = defineOutputFile(config, input, "PPSUB.INPUT.CONV.VARIANCE",
+                                                PM_FPA_FILE_VARIANCE);
+        if (!inConvVar) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to define output files");
+            return false;
+        }
     }
 
     // Convolved ref image
-    pmFPAfile *refConv = pmFPAfileDefineFromFile(config, ref, 1, 1, "PPSUB.REF.CONV");
-    if (!refConv) {
-        psError(PS_ERR_IO, false, _("Unable to generate output file for PPSUB.REF.CONV"));
-        return false;
-    }
-    if (output->type != PM_FPA_FILE_IMAGE) {
-        psError(PS_ERR_IO, true, "PPSUB.REF.CONV is not of type IMAGE");
-        return false;
-    }
-    // XXX should be based on recipe : refConv->save = true;
-
-    // Convolved ref mask
-    pmFPAfile *refConvMask = pmFPAfileDefineOutput(config, refConv->fpa, "PPSUB.REF.CONV.MASK");
-    if (!refConvMask) {
-        psError(PS_ERR_IO, false, _("Unable to generate output file from PPSUB.REF.CONV.MASK"));
-        return false;
-    }
-    if (refConvMask->type != PM_FPA_FILE_MASK) {
-        psError(PS_ERR_IO, true, "PPSUB.REF.CONV.MASK is not of type MASK");
-        return false;
-    }
-    // XXX should be based on recipe : refConvMask->save = true;
-
-    // Convolved ref variance
-    if (refVariance) {
-        pmFPAfile *refConvVariance = pmFPAfileDefineOutput(config, refConv->fpa, "PPSUB.REF.CONV.VARIANCE");
-        if (!refConvVariance) {
-            psError(PS_ERR_IO, false, _("Unable to generate output file from PPSUB.OUTPUT.VARIANCE"));
-            return false;
-        }
-        if (refConvVariance->type != PM_FPA_FILE_VARIANCE) {
-            psError(PS_ERR_IO, true, "PPSUB.REF.CONV.VARIANCE is not of type VARIANCE");
-            return false;
-        }
-        // XXX should be based on recipe : refConvVariance->save = true;
-    }
+    pmFPAfile *refConvImage = defineOutputFile(config, input, "PPSUB.REF.CONV", PM_FPA_FILE_IMAGE);
+    pmFPAfile *refConvMask = defineOutputFile(config, input, "PPSUB.REF.CONV.MASK", PM_FPA_FILE_MASK);
+    if (!refConvImage || !refConvMask) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to define output files");
+        return false;
+    }
+    if (refVar) {
+        pmFPAfile *refConvVar = defineOutputFile(config, input, "PPSUB.REF.CONV.VARIANCE",
+                                                 PM_FPA_FILE_VARIANCE);
+        if (!refConvVar) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to define output files");
+            return false;
+        }
+    }
+
 
     // Output JPEGs
@@ -230,29 +235,7 @@
 
     // Output subtraction kernel
-    pmFPAfile *outKernels = pmFPAfileDefineOutput(config, output->fpa, "PPSUB.OUTPUT.KERNELS");
-    if (!outKernels) {
-        psError(PS_ERR_IO, false, _("Unable to generate output file from PPSUB.OUTPUT.KERNELS"));
-        return false;
-    }
-    if (outKernels->type != PM_FPA_FILE_SUBKERNEL) {
-        psError(PS_ERR_IO, true, "PPSUB.OUTPUT.KERNELS is not of type SUBKERNEL");
-        return false;
-    }
-    outKernels->save = true;
-
-#if 0
-    if (!pmFPAAddSourceFromFormat(output->fpa, "Subtraction", output->format)) {
-        psError(PS_ERR_UNKNOWN, false, "Unable to generate output FPA.");
-        return false;
-    }
-#endif
-
-    pmFPAfile *sources = pmFPAfileDefineFromArgs(&status, config, "PPSUB.SOURCES", "PPSUB.SOURCES");
-    if (!status) {
-        psError(PS_ERR_IO, false, "Failed to load file definition PPSUB.SOURCES");
-        return false;
-    }
-    if (sources && sources->type != PM_FPA_FILE_CMF) {
-        psError(PS_ERR_IO, true, "PPSUB.SOURCES is not of type CMF");
+    pmFPAfile *kernel = defineCalcFile(config, output, "PPSUB.OUTPUT.KERNELS", PM_FPA_FILE_SUBKERNEL);
+    if (!kernel) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to define file PPSUB.OUTPUT.KERNELS");
         return false;
     }
Index: /branches/cnb_branches/cnb_branch_20090301/ppSub/src/ppSubLoop.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppSub/src/ppSubLoop.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ppSub/src/ppSubLoop.c	(revision 23352)
@@ -23,4 +23,9 @@
 bool ppSubLoop(pmConfig *config)
 {
+    psAssert(config, "Require configuration.");
+
+    pmConfigCamerasCull(config, NULL);
+    pmConfigRecipesCull(config, "PPSUB,PPSTATS,PSPHOT,MASKS,JPEG");
+
     bool mdok;                          // Status of MD lookup
     const char *statsName = psMetadataLookupStr(&mdok, config->arguments, "STATS"); // Filename for statistics
@@ -59,5 +64,4 @@
 
     pmFPAview *view = pmFPAviewAlloc(0); // Pointer into FPA hierarchy
-    pmHDU *lastHDU = NULL;              // Last HDU that was updated
 
     // Iterate over the FPA hierarchy
@@ -99,14 +103,4 @@
             if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
                 return false;
-            }
-
-            // Put version information into the header
-            pmHDU *hdu = pmHDUFromCell(inCell);
-            if (hdu && hdu != lastHDU) {
-                if (!hdu->header) {
-                    hdu->header = psMetadataAlloc();
-                }
-                ppSubVersionMetadata(hdu->header);
-                lastHDU = hdu;
             }
 
@@ -183,4 +177,11 @@
     }
 
+    psString dump_file = psMetadataLookupStr(&mdok, config->arguments, "-dumpconfig");
+    if (dump_file) {
+
+        pmFPAfile *input = psMetadataLookupPtr(NULL, config->files, "PPSUB.INPUT"); // Input file
+        pmConfigDump(config, input->fpa, dump_file);
+    }
+
     return true;
 }
Index: /branches/cnb_branches/cnb_branch_20090301/ppSub/src/ppSubMakePSF.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppSub/src/ppSubMakePSF.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ppSub/src/ppSubMakePSF.c	(revision 23352)
@@ -81,6 +81,5 @@
     // Extract the loaded sources from the associated readout, and generate PSF
     // Here, we assume the image is background-subtracted
-    pmReadout *sourcesRO = pmFPAfileThisReadout(config->files, view, "PPSUB.SOURCES");
-    psArray *sources = psMetadataLookupPtr(&mdok, sourcesRO->analysis, "PSPHOT.SOURCES");
+    psArray *sources = psMetadataLookupPtr(&mdok, minuend->analysis, "PSPHOT.SOURCES");
     if (!psphotReadoutFindPSF(config, view, sources)) {
         psError(PS_ERR_UNKNOWN, false, "Unable to perform photometry on subtracted image.");
Index: /branches/cnb_branches/cnb_branch_20090301/ppSub/src/ppSubMatchPSFs.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppSub/src/ppSubMatchPSFs.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ppSub/src/ppSubMatchPSFs.c	(revision 23352)
@@ -49,10 +49,34 @@
     bool mdok;                          // Status of MD lookup
 
+    // Load pre-calculated kernel, if available
+    pmReadout *kernelRO = pmFPAfileThisReadout(config->files, view, "PPSUB.OUTPUT.KERNEL"); // RO with kernel
+
     // Sources in image, used for stamps: these must be loaded from previous analysis stages
-    pmReadout *sourcesRO = pmFPAfileThisReadout(config->files, view, "PPSUB.SOURCES"); // Readout with sources
-    psArray *sources = NULL;            // Sources in image; used for stamps
-    if (sourcesRO) {
-        sources = psMetadataLookupPtr(&mdok, sourcesRO->analysis, "PSPHOT.SOURCES");
+    psArray *inSources = psMetadataLookupPtr(&mdok, inRO->analysis, "PSPHOT.SOURCES"); // Input source list
+    psArray *refSources = psMetadataLookupPtr(&mdok, refRO->analysis, "PSPHOT.SOURCES"); // Ref source list
+
+    psArray *sources = NULL;            // Merged list of sources
+    if (inSources && refSources) {
+        float radius = psMetadataLookupF32(NULL, recipe, "SOURCE.RADIUS"); // Matching radius
+        psArray *lists = psArrayAlloc(2); // Source lists
+        lists->data[0] = psMemIncrRefCounter(inSources);
+        lists->data[1] = psMemIncrRefCounter(refSources);
+        sources = pmSourceMatchMerge(lists, radius);
+        psFree(lists);
+        if (!sources) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to merge source lists");
+            return false;
+        }
+    } else if (inSources) {
+        sources = psMemIncrRefCounter(inSources);
+    } else if (refSources) {
+        sources = psMemIncrRefCounter(refSources);
     }
+
+    psMetadataAddArray(inConv->analysis, PS_LIST_TAIL, "PSPHOT.SOURCES", PS_META_REPLACE,
+                       "Merged source list", sources);
+    psMetadataAddArray(refConv->analysis, PS_LIST_TAIL, "PSPHOT.SOURCES", PS_META_REPLACE,
+                       "Merged source list", sources);
+    psFree(sources);                    // Drop reference
 
     int footprint = psMetadataLookupS32(NULL, recipe, "STAMP.FOOTPRINT"); // Stamp half-size
@@ -113,10 +137,18 @@
 
     // Match the PSFs
-    if (!pmSubtractionMatch(inConv, refConv, inRO, refRO, footprint, stride, regionSize, spacing, threshold,
-                            sources, stampsName, type, size, order, widths, orders, inner, ringsOrder,
-                            binning, penalty, optimum, optWidths, optOrder, optThresh, iter, rej, sys,
-                            maskVal, maskBad, maskPoor, poorFrac, badFrac, subMode)) {
-        psError(PS_ERR_UNKNOWN, false, "Unable to match images.");
-        return false;
+    if (kernelRO) {
+        if (!pmSubtractionMatchPrecalc(inConv, refConv, inRO, refRO, kernelRO->analysis,
+                                       stride, sys, maskVal, maskBad, maskPoor, poorFrac, badFrac)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to convolve images.");
+            return false;
+        }
+    } else {
+        if (!pmSubtractionMatch(inConv, refConv, inRO, refRO, footprint, stride, regionSize, spacing,
+                                threshold, sources, stampsName, type, size, order, widths, orders, inner,
+                                ringsOrder, binning, penalty, optimum, optWidths, optOrder, optThresh, iter,
+                                rej, sys, maskVal, maskBad, maskPoor, poorFrac, badFrac, subMode)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to match images.");
+            return false;
+        }
     }
 
@@ -129,9 +161,10 @@
 
     // XXX drop the pixels associated with inRO and refRO (now that we have inConv and refConf)
-
+#ifdef TESTING
     psphotSaveImage (NULL, inRO->image, "inRO.fits");
     psphotSaveImage (NULL, refRO->image, "refRO.fits");
     psphotSaveImage (NULL, inConv->image, "inConv.fits");
     psphotSaveImage (NULL, refConv->image, "refConv.fits");
+#endif
 
     return true;
Index: /branches/cnb_branches/cnb_branch_20090301/ppSub/src/ppSubReadoutUpdate.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppSub/src/ppSubReadoutUpdate.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ppSub/src/ppSubReadoutUpdate.c	(revision 23352)
@@ -44,4 +44,5 @@
     psMetadataAddStr(outHDU->header, PS_LIST_TAIL, "PPSUB.INPUT", 0,
                      "Subtraction input", inFile->filename);
+    ppSubVersionHeader(outHDU->header);
 
     // Statistics on the matching
Index: /branches/cnb_branches/cnb_branch_20090301/ppSub/src/ppSubVarianceFactors.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppSub/src/ppSubVarianceFactors.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ppSub/src/ppSubVarianceFactors.c	(revision 23352)
@@ -65,5 +65,5 @@
 
     psStats *vfStats = psStatsAlloc(PS_STAT_ROBUST_MEDIAN); // Statistics
-    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 0); // Random number generator
+    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS); // Random number generator
 
     // mask these values when examining the background
Index: /branches/cnb_branches/cnb_branch_20090301/ppSub/src/ppSubVersion.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppSub/src/ppSubVersion.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ppSub/src/ppSubVersion.c	(revision 23352)
@@ -19,51 +19,108 @@
 #include <psmodules.h>
 #include <ppStats.h>
+#include <psphot.h>
 
 #include "ppSub.h"
 
-static const char *cvsTag = "$Name: not supported by cvs2svn $";///< CVS tag name
+#ifndef PPSUB_VERSION
+#error "PPSUB_VERSION is not set"
+#endif
+#ifndef PPSUB_BRANCH
+#error "PPSUB_BRANCH is not set"
+#endif
+#ifndef PPSUB_SOURCE
+#error "PPSUB_SOURCE is not set"
+#endif
+
+#define xstr(s) str(s)
+#define str(s) #s
 
 psString ppSubVersion(void)
 {
-    psString version = NULL;            // Version, to return
-    psStringAppend(&version, "%s-%s",PACKAGE_NAME,PACKAGE_VERSION);
-    return version;
+    char *value = NULL;
+    psStringAppend(&value, "%s@%s", xstr(PPSUB_BRANCH), xstr(PPSUB_VERSION));
+    return value;
+}
+
+psString ppSubSource(void)
+{
+    return psStringCopy(xstr(PPSUB_SOURCE));
 }
 
 psString ppSubVersionLong(void)
 {
-    psString version = ppSubVersion(); // Version, to return
-    psString tag = psStringStripCVS(cvsTag, "Name"); // CVS tag
-    psStringAppend(&version, " (cvs tag %s) %s, %s", tag, __DATE__, __TIME__);
-    psFree(tag);
+    psString version = ppSubVersion();  // Version, to return
+    psString source = ppSubSource();    // Source
+
+    psStringPrepend(&version, "ppSub ");
+    psStringAppend(&version, " from %s, built %s, %s", source, __DATE__, __TIME__);
+    psFree(source);
+
+#ifdef __OPTIMIZE__
+    psStringAppend(&version, " optimised");
+#else
+    psStringAppend(&version, " unoptimised");
+#endif
+
     return version;
-}
+};
 
 
-void ppSubVersionMetadata(psMetadata *metadata)
+bool ppSubVersionHeader(psMetadata *header)
 {
-    PS_ASSERT_METADATA_NON_NULL(metadata,);
-
-    psString pslib = psLibVersionLong();// psLib version
-    psString psmodules = psModulesVersionLong(); // psModules version
-    psString ppStats = ppStatsVersionLong(); // ppStats version
-    psString ppSub = ppSubVersionLong(); // ppSub version
+    PS_ASSERT_METADATA_NON_NULL(header, false);
 
     psTime *time = psTimeGetNow(PS_TIME_TAI); // The time now
     psString timeString = psTimeToISO(time); // The time in an ISO string
     psFree(time);
-    psString head = NULL;               // Head string
-    psStringAppend(&head, "ppSub processing at %s. Component information:", timeString);
+    psString history = NULL;               // History string
+    psStringAppend(&history, "ppSub at %s", timeString);
+    psFree(timeString);
+    psMetadataAddStr(header, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, NULL, history);
+    psFree(history);
+
+    psLibVersionHeader(header);
+    psModulesVersionHeader(header);
+    psphotVersionHeader(header);
+    ppStatsVersionHeader(header);
+
+    psString version = ppSubVersion(); // Software version
+    psString source  = ppSubSource();  // Software source
+
+    psStringPrepend(&version, "ppSub version: ");
+    psStringPrepend(&source, "ppSub source: ");
+
+    psMetadataAddStr(header, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, NULL, version);
+    psMetadataAddStr(header, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, NULL, source);
+
+    psFree(version);
+    psFree(source);
+
+    return true;
+}
+
+void ppSubVersionPrint(void)
+{
+    psTime *time = psTimeGetNow(PS_TIME_TAI); // The time now
+    psString timeString = psTimeToISO(time); // The time in an ISO string
+    psFree(time);
+    psLogMsg("ppSub", PS_LOG_INFO, "ppSub at %s", timeString);
     psFree(timeString);
 
-    psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, head, "");
-    psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, pslib, "");
-    psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, psmodules, "");
-    psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, ppStats, "");
-    psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, ppSub, "");
+    psString pslib = psLibVersionLong();// psLib version
+    psString psmodules = psModulesVersionLong(); // psModules version
+    psString psphot = psphotVersionLong(); // psphot version
+    psString ppStats = ppStatsVersionLong(); // ppStats version
+    psString ppSub = ppSubVersionLong(); // ppSub version
 
-    psFree(head);
+    psLogMsg("ppSub", PS_LOG_INFO, "%s", pslib);
+    psLogMsg("ppSub", PS_LOG_INFO, "%s", psmodules);
+    psLogMsg("ppSub", PS_LOG_INFO, "%s", psphot);
+    psLogMsg("ppSub", PS_LOG_INFO, "%s", ppStats);
+    psLogMsg("ppSub", PS_LOG_INFO, "%s", ppSub);
+
     psFree(pslib);
     psFree(psmodules);
+    psFree(psphot);
     psFree(ppStats);
     psFree(ppSub);
Index: /branches/cnb_branches/cnb_branch_20090301/ppSub/test/fake.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppSub/test/fake.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/ppSub/test/fake.c	(revision 23352)
@@ -89,5 +89,5 @@
 int main(int argc, char *argv[])
 {
-    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 0); // Random number generator
+    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS); // Random number generator
 
     // Images to generate
Index: /branches/cnb_branches/cnb_branch_20090301/psLib/configure.ac
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psLib/configure.ac	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/psLib/configure.ac	(revision 23352)
@@ -122,40 +122,23 @@
 TMP_CPPFLAGS=${CPPFLAGS}
 
-AC_ARG_WITH(cfitsio,
-[AS_HELP_STRING(--with-cfitsio=DIR,Specify location of CFITSIO.)],
-[CFITSIO_CFLAGS="-I$withval/include"
- CFITSIO_LDFLAGS="-L$withval/lib"])
-AC_ARG_WITH(cfitsio-include,
-[AS_HELP_STRING(--with-cfitsio-include=DIR,Specify CFITSIO include directory.)],
-[CFITSIO_CFLAGS="-I$withval"])
-AC_ARG_WITH(cfitsio-lib,
-[AS_HELP_STRING(--with-cfitsio-lib=DIR,Specify CFITSIO library directory.)],
-[CFITSIO_LDFLAGS="-L$withval"])
+PKG_CHECK_MODULES([CFITSIO], [cfitsio], [], AC_MSG_ERROR([CFITSIO package not found.  Obtain CFITSIO at  http://heasarc.gsfc.nasa.gov/docs/software/fitsio]))
+
 PSLIB_CFLAGS="${PSLIB_CFLAGS=} ${CFITSIO_CFLAGS}"
-PSLIB_LIBS="${PSLIB_LIBS=} $CFITSIO_LDFLAGS -lcfitsio -lm"
+PSLIB_LIBS="${PSLIB_LIBS=} ${CFITSIO_LIBS}"
 
 CFLAGS="${CFLAGS=} ${CFITSIO_CFLAGS}"
-LDFLAGS="${LDFLAGS=} ${CFITSIO_LDFLAGS}"
-
-dnl Solaris needs to suck in these symbols from unusual locations
-AC_SEARCH_LIBS([gethostbyname], [nsl])
-AC_SEARCH_LIBS([socket], [socket])
-
-AC_CHECK_HEADERS([fitsio.h],[],
-   [AC_MSG_ERROR([CFITSIO headers not found.  Obtain CFITSIO at  http://heasarc.gsfc.nasa.gov/docs/software/fitsio or use --with-cfitsio to specify location.])]
-)
-TMP_LIBS=${LIBS}
-AC_CHECK_LIB(cfitsio,ffopen,[],
-  [AC_MSG_ERROR([CFITSIO library not found.  Obtain it at  http://heasarc.gsfc.nasa.gov/docs/software/fitsio or use --with-cfitsio to specify location.])],[-lm]
-)
+LDFLAGS="${LDFLAGS=} ${CFITSIO_LIBS}"
+
 dnl Now check if CFITSIO supports fits_open_diskfile, i.e., is at least version 2.501
-AC_CHECK_LIB(cfitsio,ffdkopn,
+AC_CHECK_FUNC(ffdkopn,
   [CFITSIO_DISKFILE=1],
   [AC_MSG_WARN([The CFITSIO library version is rather old.  Suggested version is 2.501 or greater.])
-   CFITSIO_DISKFILE=0],[-lm]
+   CFITSIO_DISKFILE=0]
 )
 
 AC_DEFINE_UNQUOTED([CFITSIO_DISKFILE],${CFITSIO_DISKFILE},[Define to 1 if you have fits_open_diskfile in CFITSIO])
+
 AC_SUBST([CFITSIO_CFLAGS])
+AC_SUBST([CFITSIO_LIBS])
 
 dnl restore the LIBS/CFLAGS/LDFLAGS
@@ -173,44 +156,20 @@
 TMP_CPPFLAGS=${CPPFLAGS}
 
-AC_ARG_WITH(fftw3,
-[AS_HELP_STRING(--with-fftw3=DIR,Specify location of FFTW version 3.)],
-[FFTW3_CFLAGS="-I$withval/include"
- FFTW3_LDFLAGS="-L$withval/lib"])
-AC_ARG_WITH(fftw3-include,
-[AS_HELP_STRING(--with-fftw3-include=DIR,Specify FFTW version 3 include directory.)],
-[FFTW3_CFLAGS="-I$withval"])
-AC_ARG_WITH(fftw3-lib,
-[AS_HELP_STRING(--with-fftw3-lib=DIR,Specify FFTW version 3 library directory.)],
-[FFTW3_LDFLAGS="-L$withval"])
+PKG_CHECK_MODULES([FFTW3], [fftw3f], [], AC_MSG_ERROR([FFTW version 3 (--enable-float) library not found.  Obtain it at http://www.fftw.org/]))
+
 PSLIB_CFLAGS="${PSLIB_CFLAGS=} ${FFTW3_CFLAGS}"
-PSLIB_LIBS="${PSLIB_LIBS=} $FFTW3_LDFLAGS -lfftw3f"
+PSLIB_LIBS="${PSLIB_LIBS=} ${FFTW3_LIBS}"
 
 CFLAGS="${CFLAGS} ${FFTW3_CFLAGS}"
 CPPFLAGS=${CFLAGS}
-LDFLAGS="${LDFLAGS} ${FFTW3_LDFLAGS}"
-
-AC_CHECK_LIB(fftw3f,fftwf_plan_dft_2d,[],
-  [AC_MSG_ERROR([FFTW version 3 (--enable-float) library not found.  Obtain it at http://www.fftw.org/ or use --with-fftw3 to specify location.])])
-
-FFTW_THREADS=0
-AC_CHECK_LIB(fftw3f,fftwf_init_threads,[FFTW_THREADS=1],
-  [AC_CHECK_LIB(fftw3f_threads,fftwf_init_threads, [
-    FFTW_THREADS=1
-    PSLIB_LIBS="${PSLIB_LIBS=} -lfftw3f_threads"],
-   AC_MSG_WARN([FFTW version 3 not compiled with thread support (--enable-threads)]),[-lm]
-   )],[-lm]
-)
-
-dnl AC_CHECK_LIB(fftw3f,fftwf_plan_dft_2d,[],
-dnl   [AC_MSG_ERROR([FFTW version 3 (--enable-float) library not found.  Obtain it at http://www.fftw.org/ or use --with-fftw3 to specify location.])],[-lm]
-dnl )
-
-AC_CHECK_HEADERS([fftw3.h],[],
-  [AC_MSG_ERROR([FFTW version 3 (--enable-float) headers not found.  Obtain it at http://www.fftw.org/ or use --with-fftw3 to specify location.])]
-)
-
+LDFLAGS="${LDFLAGS} ${FFTW3_LIBS}"
+
+AC_CHECK_FUNC(fftwf_plan_dft_2d,[],
+  [AC_MSG_ERROR([FFTW version 3 (--enable-float) library not found.  Obtain it at http://www.fftw.org/])])
+AC_CHECK_FUNC(fftwf_init_threads,[FFTW_THREADS=1],[FFTW_THREADS=0])
 AC_DEFINE_UNQUOTED([HAVE_FFTW_THREADS],${FFTW_THREADS},[Define to 1 if you have FFTW compiled with thread support])
 
 AC_SUBST([FFTW3_CFLAGS])
+AC_SUBST([FFTW3_LIBS])
 
 dnl restore the CFLAGS/LDFLAGS
@@ -228,23 +187,11 @@
 TMP_CPPFLAGS=${CPPFLAGS}
 
-AC_ARG_WITH(gsl-config,
-[AS_HELP_STRING(--with-gsl-config=FILE,Specify location of gsl-config.)],
-[GSL_CONFIG=$withval],
-[GSL_CONFIG=`which gsl-config`])
-AC_CHECK_FILE($GSL_CONFIG,[],
-    [AC_MSG_ERROR([GSL is required.  Obtain it at http://www.gnu.org/software/gsl or use --with-gsl-config to specify location.])])
-
-AC_MSG_CHECKING([GSL cflags])
-GSL_CFLAGS="`${GSL_CONFIG} --cflags`"
-AC_MSG_RESULT([${GSL_CFLAGS}])
-
-AC_MSG_CHECKING([GSL ldflags])
-GSL_LDFLAGS="`${GSL_CONFIG} --libs`"
-AC_MSG_RESULT([${GSL_LDFLAGS}])
+PKG_CHECK_MODULES([GSL], [gsl], [], AC_MSG_ERROR([GSL is required.  Obtain it at http://www.gnu.org/software/gsl]))
 
 PSLIB_CFLAGS="${PSLIB_CFLAGS=} ${GSL_CFLAGS}"
-PSLIB_LIBS="${PSLIB_LIBS=} ${GSL_LDFLAGS}"
+PSLIB_LIBS="${PSLIB_LIBS=} ${GSL_LIBS}"
 
 AC_SUBST([GSL_CFLAGS])
+AC_SUBST([GSL_LIBS])
 
 dnl restore the CFLAGS/LDFLAGS
@@ -277,10 +224,10 @@
 LDFLAGS="${LDFLAGS} ${JPEG_LDFLAGS}"
 
-AC_CHECK_HEADERS([jpeglib.h],[PSLIB_CFLAGS="$PSLIB_CFLAGS $JPEG_CFLAGS"
+AC_CHECK_HEADERS([jpeglib.h],[PSLIB_CFLAGS="${PSLIB_CFLAGS=} ${JPEG_CFLAGS}"
 			      AC_SUBST(JPEG_CFLAGS)],
   [AC_MSG_ERROR([libjpeg headers not found.  Obtain libjpeg from http://www.ijg.org/ or use --with-jpeg to specify location.])]
 )
 
-AC_CHECK_LIB(jpeg,jpeg_CreateCompress,[PSLIB_LIBS="$PSLIB_LIBS $JPEG_LDFLAGS -ljpeg"],
+AC_CHECK_LIB(jpeg,jpeg_CreateCompress,[PSLIB_LIBS="${PSLIB_LIBS=} ${JPEG_LDFLAGS} -ljpeg"],
   [AC_MSG_ERROR([libjpeg library not found.  Obtain libjpeg from http://www.ijg.org/ or use --with-jpeg to specify location.])]
 )
@@ -291,37 +238,4 @@
 LDFLAGS=${TMP_LDFLAGS}
 CPPFLAGS=${TMP_CPPFLAGS}
-
-dnl ------------------- XML2 options ---------------------
-dnl AC_ARG_WITH(xml2-config,
-dnl [AS_HELP_STRING(--with-xml2-config=FILE,Specify location of xml2-config.)],
-dnl [XML_CONFIG=$withval],
-dnl [XML_CONFIG=`which xml2-config`])
-dnl AC_CHECK_FILE($XML_CONFIG,[],
-dnl     [AC_MSG_ERROR([GNOME XML C parser is required.  Obtain it at http://www.xmlsoft.org or use --with-xml2-config to specify location.])])
-dnl
-dnl AC_MSG_CHECKING([xml2 version])
-dnl XML_VERSION=`xml2-config --version`
-dnl XML_VERSION_major=`echo $XML_VERSION | ${PERL} -pe 's|^(\d+).*|\1|'`
-dnl XML_VERSION_minor=`echo $XML_VERSION | ${PERL} -pe 's|^(\d+)\.(\d+).*|\2|'`
-dnl dnl First test the minimum version of 2.6
-dnl if test $XML_VERSION_major -lt 2 || ( test $XML_VERSION_major -eq 2 && test $XML_VERSION_minor -lt 6 )
-dnl then
-dnl 	AC_MSG_ERROR([requires libxml2 2.6.0 or greater, found $XML_VERSION.  Install newer version or use --with-xml2-config to specify another location.])
-dnl else
-dnl     AC_MSG_RESULT([$XML_VERSION... yes])
-dnl fi
-dnl
-dnl AC_MSG_CHECKING([xml2 cflags])
-dnl XML_CFLAGS="`${XML_CONFIG} --cflags`"
-dnl AC_MSG_RESULT([${XML_CFLAGS}])
-dnl
-dnl AC_MSG_CHECKING([xml2 ldflags])
-dnl XML_LDFLAGS="`${XML_CONFIG} --libs`"
-dnl AC_MSG_RESULT([${XML_LDFLAGS}])
-dnl
-dnl PSLIB_CFLAGS="${PSLIB_CFLAGS=} ${XML_CFLAGS}"
-dnl PSLIB_LIBS="${PSLIB_LIBS=} ${XML_LDFLAGS}"
-dnl
-dnl AC_SUBST([XML_CFLAGS])
 
 dnl ------------------- SWIG options ---------------------
@@ -477,7 +391,4 @@
   utils/Makefile
 ])
-dnl src/xml/Makefile
-dnl test/xml/Makefile
-
 
 #if test "$SWIG_REQ" == "yes"
Index: /branches/cnb_branches/cnb_branch_20090301/psLib/src/fits/psFitsHeader.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psLib/src/fits/psFitsHeader.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/psLib/src/fits/psFitsHeader.c	(revision 23352)
@@ -149,5 +149,5 @@
 
 
-bool psFitsCheckSingleCompressedImagePHU(const psFits *fits, psMetadata *header)
+bool psFitsCheckCompressedImagePHU(const psFits *fits, psMetadata *header)
 {
     PS_ASSERT_FITS_NON_NULL(fits, false);
@@ -163,6 +163,6 @@
     }
 
-    if (psFitsGetSize(fits) != 2) {
-        // No second extension, or multiple extensions
+    if (psFitsGetSize(fits) == 1) {
+        // No extension present
         return false;
     }
@@ -416,5 +416,5 @@
     // Explore the potential case that this is an empty PHU, and the first extension contains the sole image,
     // which is compressed.
-    if (psFitsCheckSingleCompressedImagePHU(fits, header)) {
+    if (psFitsCheckCompressedImagePHU(fits, header)) {
         // This is really what we want, not the empty PHU
         psTrace("psLib.fits", 1,
Index: /branches/cnb_branches/cnb_branch_20090301/psLib/src/fits/psFitsHeader.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psLib/src/fits/psFitsHeader.h	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/psLib/src/fits/psFitsHeader.h	(revision 23352)
@@ -20,5 +20,5 @@
 
 
-/// Determine whether the current HDU is an empty PHU with a single compressed image following.
+/// Determine whether the current HDU is an empty PHU with a compressed image following.
 ///
 /// In that case, what should be treated as an image PHU is technically an empty PHU with a binary table
@@ -26,6 +26,6 @@
 /// following compressed image to determine if this is the case.  If so, the FITS file pointer is left
 /// pointing at the compressed image.
-bool psFitsCheckSingleCompressedImagePHU(const psFits *fits, ///< FITS file pointer
-                                         const psMetadata *header ///< Header, or NULL
+bool psFitsCheckCompressedImagePHU(const psFits *fits, ///< FITS file pointer
+                                   const psMetadata *header ///< Header, or NULL
     );
 
Index: /branches/cnb_branches/cnb_branch_20090301/psLib/src/fits/psFitsImage.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psLib/src/fits/psFitsImage.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/psLib/src/fits/psFitsImage.c	(revision 23352)
@@ -219,5 +219,5 @@
     PS_ASSERT_FITS_NON_NULL(fits, NULL);
 
-    if (psFitsCheckSingleCompressedImagePHU(fits, NULL)) {
+    if (psFitsCheckCompressedImagePHU(fits, NULL)) {
         // This is really what we want, not the empty PHU
         psTrace("psLib.fits", 1,
@@ -432,5 +432,5 @@
     PS_ASSERT_INT_NONNEGATIVE(z, NULL);
 
-    if (psFitsCheckSingleCompressedImagePHU(fits, NULL)) {
+    if (psFitsCheckCompressedImagePHU(fits, NULL)) {
         // This is really what we want, not the empty PHU
         psTrace("psLib.fits", 1,
@@ -479,5 +479,5 @@
     }
 
-    if (psFitsCheckSingleCompressedImagePHU(fits, NULL)) {
+    if (psFitsCheckCompressedImagePHU(fits, NULL)) {
         // This is really what we want, not the empty PHU
         psTrace("psLib.fits", 1,
@@ -873,5 +873,5 @@
     // code replication, and should be sufficient for our needs.
 
-    if (psFitsCheckSingleCompressedImagePHU(fits, NULL)) {
+    if (psFitsCheckCompressedImagePHU(fits, NULL)) {
         // This is really what we want, not the empty PHU
         psTrace("psLib.fits", 1,
Index: /branches/cnb_branches/cnb_branch_20090301/psLib/src/fits/psFitsScale.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psLib/src/fits/psFitsScale.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/psLib/src/fits/psFitsScale.c	(revision 23352)
@@ -112,6 +112,5 @@
     // psImageBackground automatically excludes pixels that are non-finite, so we don't need to bother about a
     // mask.
-    psU64 seed = p_psRandomGetSystemSeed(false);
-    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, seed);
+    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS);
     psStats *stats = psStatsAlloc(MEAN_STAT | STDEV_STAT); // Statistics object
     if (!psImageBackground(stats, NULL, image, mask, maskVal, rng)) {
@@ -290,6 +289,5 @@
     if (!psMemIncrRefCounter(rng) && options->fuzz) {
         // Don't blab about which seed we're going to get --- it's not necessary for this purpose
-        psU64 seed = p_psRandomGetSystemSeed(false);
-        rng = psRandomAlloc(PS_RANDOM_TAUS, seed);
+        rng = psRandomAlloc(PS_RANDOM_TAUS);
     }
 
Index: /branches/cnb_branches/cnb_branch_20090301/psLib/src/imageops/psImageCovariance.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psLib/src/imageops/psImageCovariance.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/psLib/src/imageops/psImageCovariance.c	(revision 23352)
@@ -43,4 +43,26 @@
     } else {
         covar = psImageCovarianceNone();
+    }
+
+    // Check for non-finite elements
+    for (int y = kernel->yMin; y <= kernel->yMax; y++) {
+        for (int x = kernel->xMin; x <= kernel->xMax; x++) {
+            if (!isfinite(kernel->kernel[y][x])) {
+                psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                        "Non-finite covariance matrix element in kernel at %d,%d", x, y);
+                psFree(covar);
+                return NULL;
+            }
+        }
+    }
+    for (int y = covar->yMin; y <= covar->yMax; y++) {
+        for (int x = covar->xMin; x <= covar->xMax; x++) {
+            if (!isfinite(covar->kernel[y][x])) {
+                psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                        "Non-finite covariance matrix element in covariance matrix at %d,%d", x, y);
+                psFree(covar);
+                return NULL;
+            }
+        }
     }
 
@@ -110,5 +132,5 @@
 float psImageCovarianceFactor(const psKernel *covariance)
 {
-    return covariance ? covariance->kernel[0][0] : NAN;
+    return covariance ? covariance->kernel[0][0] : 1.0;
 }
 
@@ -144,4 +166,11 @@
         for (int y = covar->yMin; y <= covar->yMax; y++) {
             for (int x = covar->xMin; x <= covar->xMax; x++) {
+                if (!isfinite(covar->kernel[y][x])) {
+                    psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                            "Non-finite covariance matrix element at %d,%d for input %d",
+                            x, y, i);
+                    psFree(sum);
+                    return NULL;
+                }
                 sum->kernel[y][x] += covar->kernel[y][x];
             }
@@ -192,4 +221,9 @@
             int radius = PS_MAX(abs(x), abs(y)); // Squarish radius
             psAssert(radius <= maxRadius, "Radius doesn't fit");
+            if (!isfinite(covar->kernel[y][x])) {
+                psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Non-finite covariance matrix element at %d,%d",
+                        x, y);
+                return NULL;
+            }
             radiusSum->data.F64[radius] += fabsf(covar->kernel[y][x]);
             sum += fabsf(covar->kernel[y][x]);
Index: /branches/cnb_branches/cnb_branch_20090301/psLib/src/imageops/psImageInterpolate.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psLib/src/imageops/psImageInterpolate.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/psLib/src/imageops/psImageInterpolate.c	(revision 23352)
@@ -133,7 +133,7 @@
     }
 
-    float norm1 = 2.0 / PS_SQR(M_PI); // Normalisation for laczos
-    float norm2 = M_PI * 4.0 / (float)size; // Normalisation for sinc function 1
-    float norm3 = M_PI_2 * 4.0 / (float)size; // Normalisation for sinc function 2
+    float norm1 = size / 2.0 / PS_SQR(M_PI); // Normalisation for laczos
+    float norm2 = M_PI;                 // Normalisation for sinc function 1
+    float norm3 = M_PI * 2.0 / (float)size; // Normalisation for sinc function 2
     float pos = - (size - 1)/2 - frac;  // Position of interest
     for (int i = 0; i < size; i++, pos += 1.0) {
Index: /branches/cnb_branches/cnb_branch_20090301/psLib/src/math/psRandom.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psLib/src/math/psRandom.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/psLib/src/math/psRandom.c	(revision 23352)
@@ -29,4 +29,5 @@
 #include <inttypes.h>
 
+#include "psAbort.h"
 #include "psMemory.h"
 #include "psRandom.h"
@@ -38,19 +39,24 @@
 
 
+unsigned long seed = 0;                 // Seed for RNG
+
+
 psU64 p_psRandomGetSystemSeed(bool log)
 {
-    FILE*  fd;
-    psU64  seedVal = 0;
-    time_t timeVal;
+    psU64 seedVal = 0;                  // Seed value to return
 
-    fd = fopen("/dev/urandom","r");
-    if(fd == NULL) {
-        // Read system clock to get seed
-        seedVal = (psU64)time(&timeVal);
-    } else {
-        // Read urandom to get seed
-        if (fread(&seedVal, sizeof(psU64),1,fd)) {;} // ignore return value
-        // Close file
-        fclose(fd);
+    // Since zero is a special value in our context, don't allow the final value chosen to be zero
+    while (seedVal == 0) {
+        FILE *fd = fopen("/dev/urandom", "r");
+        if (fd) {
+            // Read urandom to get seed
+            if (fread(&seedVal, sizeof(psU64), 1, fd)) {;} // ignore return value
+            // Close file
+            fclose(fd);
+        } else {
+            // Read system clock to get seed
+            time_t timeVal;                 // Time value
+            seedVal = (psU64)time(&timeVal);
+        }
     }
 
@@ -63,100 +69,94 @@
 }
 
-psRandom *psRandomAlloc(psRandomType type,
-                        unsigned long seed)
+psU64 psRandomSeed(psU64 value)
 {
-    gsl_rng   *r      = NULL;
-    psRandom  *myRNG  = NULL;
+    while (value == 0) {
+        value = p_psRandomGetSystemSeed(false);
+    }
+    seed = value;
+    return seed;
+}
 
+// Destructor for psRandom
+static void randomFree(psRandom *rng)
+{
+    if (rng->gsl) {
+        gsl_rng_free(rng->gsl);
+    }
+    return;
+}
+
+// Constructor for psRandom
+static psRandom *randomAlloc(psRandomType type)
+{
+    psRandom *rng = psAlloc(sizeof(psRandom)); // Random number generator to return
+    psMemSetDeallocator(rng, (psFreeFunc)randomFree);
+
+    rng->type = type;
+
+    const gsl_rng_type *gslType;        // Type of RNG according to GSL
     switch (type) {
-    case PS_RANDOM_TAUS:
-        myRNG = (psRandom*)psAlloc(sizeof(psRandom));
-        r = gsl_rng_alloc(gsl_rng_taus);
-        myRNG->gsl = r;
-        if(seed == 0) {
-            gsl_rng_set(myRNG->gsl, p_psRandomGetSystemSeed(true));
-        } else {
-            gsl_rng_set(myRNG->gsl, seed);
-        }
-        myRNG->type = type;
+      case PS_RANDOM_TAUS:
+        gslType = gsl_rng_taus;
         break;
-
-    default:
-        psError(PS_ERR_UNEXPECTED_NULL, true, _("Unknown Random Number Generator Type"));
+      default:
+        psAbort("Unknown Random Number Generator Type: %x", type);
         break;
     }
 
-    return(myRNG);
+    rng->gsl = gsl_rng_alloc(gslType);
+    return rng;
 }
 
-void psRandomReset(psRandom *rand,
-                   unsigned long seed)
+psRandom *psRandomAlloc(psRandomType type)
 {
-    // Check null psRandom
-    if(rand==NULL) {
-        psError(PS_ERR_UNEXPECTED_NULL,
-                true,
-                _("Random variable is NULL."));
-    } else {
-        // Check seed value to see if system seed should be used
-        if(seed == 0) {
-            gsl_rng_set(rand->gsl,p_psRandomGetSystemSeed(true));
-        } else {
-            gsl_rng_set(rand->gsl, seed);
-        }
+    psRandom *rng = randomAlloc(type);
+    psRandomReset(rng);
+
+    return rng;
+}
+
+psRandom *psRandomAllocSpecific(psRandomType type, psU64 specificSeed)
+{
+    psRandom *rng = randomAlloc(type);
+    if (specificSeed == 0) {
+        specificSeed = p_psRandomGetSystemSeed(true);
     }
+    gsl_rng_set(rng->gsl, specificSeed);
+    return rng;
+}
+
+bool psRandomReset(psRandom *rand)
+{
+    PS_ASSERT_RANDOM_NON_NULL(rand, false);
+    if (seed == 0) {
+        seed = p_psRandomGetSystemSeed(true);
+    }
+    gsl_rng_set(rand->gsl, seed);
+    return true;
 }
 
 double psRandomUniform(const psRandom *r)
 {
-    // Check null psRandom variable
-    if(r == NULL) {
-        psError(PS_ERR_UNEXPECTED_NULL,
-                true,
-                _("Random variable is NULL."));
-        return(0);
-    } else {
-        return(gsl_rng_uniform(r->gsl));
-    }
+    PS_ASSERT_RANDOM_NON_NULL(r, NAN);
+    return gsl_rng_uniform(r->gsl);
 }
 
 double psRandomGaussian(const psRandom *r)
 {
-    // Check null psRandom variable
-    if(r == NULL) {
-        psError(PS_ERR_UNEXPECTED_NULL,
-                true,
-                _("Random variable is NULL."));
-        return(0);
-    } else {
-        // XXX: What should sigma be?
-        return(gsl_ran_gaussian(r->gsl, 1.0));
-    }
+    PS_ASSERT_RANDOM_NON_NULL(r, NAN);
+    return gsl_ran_gaussian(r->gsl, 1.0);
 }
 
 double p_psRandomGaussian(const psRandom *r, double sigma)
 {
-    // Check null psRandom variable
-    if(r == NULL) {
-        psError(PS_ERR_UNEXPECTED_NULL,
-                true,
-                _("Random variable is NULL."));
-        return(0);
-    } else {
-        return(gsl_ran_gaussian(r->gsl, sigma));
-    }
+    PS_ASSERT_RANDOM_NON_NULL(r, NAN);
+    return gsl_ran_gaussian(r->gsl, sigma);
 }
 
 double psRandomPoisson(const psRandom *r, double mean)
 {
-    // Check null psRandom variable
-    if(r == NULL) {
-        psError(PS_ERR_UNEXPECTED_NULL,
-                true,
-                _("Random variable is NULL."));
-        return(0);
-    } else {
-        return((psF64) gsl_ran_poisson(r->gsl, mean));
-    }
+    PS_ASSERT_RANDOM_NON_NULL(r, NAN);
+    return gsl_ran_poisson(r->gsl, mean);
 }
 
Index: /branches/cnb_branches/cnb_branch_20090301/psLib/src/math/psRandom.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psLib/src/math/psRandom.h	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/psLib/src/math/psRandom.h	(revision 23352)
@@ -29,12 +29,10 @@
 #include <gsl/gsl_randist.h>
 
-/** Enumeration containing a flag for psRandom types.  */
+/// Random number generator types
 typedef enum {
     PS_RANDOM_TAUS                     ///< A maximally equidistributed combined Tausworthe generator.
 } psRandomType;
 
-/** Data structure for psRandom.
- *  Contains information on the psRandom type and GNU Scientific Library random number generator.
- */
+/// Random number generator
 typedef struct {
     psRandomType type;                 ///< The type of RNG
@@ -48,20 +46,28 @@
     );
 
-/** Allocates a psRandom struct.
- *
- *  @return psRandom*:    A new psRandom structure.
- */
+/// Set the seed to use for random number generators.
+///
+/// A seed value of zero indicates that the seed is to be generated from the system.
+/// The new seed value is returned
+psU64 psRandomSeed(psU64 seed           ///< Seed for RNG
+                   );
+
+/// Allocate a random number generator
+///
+/// The currently defined seed (via psRandomSeed) is used
 psRandom *psRandomAlloc(
-    psRandomType type,                 ///< The type of RNG
-    unsigned long seed                 ///< Known value with which to seed the RNG
+    psRandomType type                   ///< The type of RNG
 ) PS_ATTR_MALLOC;
 
-/** Resets an existing psRandom struct.
- *
- *  @return void
- */
-void psRandomReset(
-    psRandom *rand,                    ///< Existing psRandom struct to reset
-    unsigned long seed                 ///< Known value with which to seed the RNG
+/// Allocate a random number generator with a specific seed
+///
+/// A seed value of zero indicates that the seed is to be generated from the system.
+psRandom *psRandomAllocSpecific(psRandomType type, ///< The type of RNG
+                                psU64 specificSeed ///< The specific seed to use
+    );
+
+/// Resets an existing random number generator
+bool psRandomReset(
+    psRandom *rand                      ///< Random number generator to reset
 );
 
@@ -72,5 +78,5 @@
  */
 double psRandomUniform(
-    const psRandom *r                  ///< psRandom struct for RNG
+    const psRandom *r                  ///< Random number generator
 );
 
@@ -81,17 +87,14 @@
  */
 double psRandomGaussian(
-    const psRandom *r                  ///< psRandom struct for RNG
+    const psRandom *r                  ///< Random number generator
 );
 
-/** Random number generator based on a Gaussian deviate, N(0,1).
+/** Random number generator based on a Gaussian deviate with specified standard deviation.
  *  Uses gsl_ran_gaussian.
- *
- *  XXX: I created this since the above psLib spec for p_psRandomGaussian
- *  had no argument for sigma.  Verify that with IfA.
  *
  *  @return double:     Random number.
  */
 double p_psRandomGaussian(
-    const psRandom *r,                  ///< psRandom struct for RNG
+    const psRandom *r,                  ///< Random number generator
     double sigma
 );
@@ -103,5 +106,5 @@
  */
 double psRandomPoisson(
-    const psRandom *r,                 ///< psRandom struct for RNG
+    const psRandom *r,                  ///< Random number generator
     double mean                         ///< Mean value
 );
Index: /branches/cnb_branches/cnb_branch_20090301/psLib/src/math/psStats.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psLib/src/math/psStats.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/psLib/src/math/psStats.c	(revision 23352)
@@ -199,4 +199,6 @@
     for (long i = 0; i < numData; i++) {
         // Check if the data is with the specified range
+	if (!isfinite(data[i])) 
+	    continue;
         if (useRange && (data[i] < stats->min))
             continue;
@@ -322,4 +324,6 @@
     // into the temporary vectors.
     for (long i = 0; i < inVector->n; i++) {
+	if (!isfinite(input[i])) 
+	    continue;
         if (useRange && (input[i] < stats->min))
             continue;
@@ -420,4 +424,6 @@
     for (long i = 0; i < myVector->n; i++) {
         // Check if the data is with the specified range
+	if (!isfinite(data[i])) 
+	    continue;
         if (useRange && (data[i] < stats->min)) {
             continue;
@@ -501,4 +507,6 @@
     for (long i = 0; i < myVector->n; i++) {
         // Check if the data is with the specified range
+	if (!isfinite(data[i])) 
+	    continue;
         if (useRange && (data[i] < stats->min)) {
             continue;
Index: /branches/cnb_branches/cnb_branch_20090301/psLib/src/pslib_strict.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psLib/src/pslib_strict.h	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/psLib/src/pslib_strict.h	(revision 23352)
@@ -110,4 +110,5 @@
 #include "psMetadataItemParse.h"
 #include "psMetadataItemCompare.h"
+#include "psMetadataHeader.h"
 #include "psPixels.h"
 #include "psArguments.h"
Index: /branches/cnb_branches/cnb_branch_20090301/psLib/src/sys/Makefile.am
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psLib/src/sys/Makefile.am	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/psLib/src/sys/Makefile.am	(revision 23352)
@@ -3,5 +3,14 @@
 noinst_LTLIBRARIES = libpslibsys.la
 
-libpslibsys_la_CPPFLAGS = $(SRCINC) $(PSLIB_CFLAGS) $(CFITSIO_CFLAGS)
+# PSLIB_VERSION=`if [ -e ../../VERSION ]; then cat ../../VERSION; else svnversion; fi`
+# PSLIB_BRANCH=`if [ -e ../../BRANCH ]; then cat ../../BRANCH; else svn info | sed -n -e '/URL:/ h' -e '/Repository Root:/ { x; H; x; s|Repository Root: \(.*\)\nURL: \1\(.*\)|\2| ; s|^/|| ; s|/[a-zA-Z]*/src.*|| ; p }'; fi`
+# PSLIB_SOURCE=`if [ -e ../../SOURCE ]; then cat ../../SOURCE; else svn info | sed -n -e 's/Repository UUID: // p'; fi`
+
+# Force recompilation of psConfigure.c, since it gets the version information
+psConfigure.c: FORCE
+	touch psConfigure.c
+FORCE: ;
+
+libpslibsys_la_CPPFLAGS = $(SRCINC) $(PSLIB_CFLAGS) $(CFITSIO_CFLAGS) -DPSLIB_VERSION=$(SVN_VERSION) -DPSLIB_BRANCH=$(SVN_BRANCH) -DPSLIB_SOURCE=$(SVN_SOURCE)
 libpslibsys_la_SOURCES = \
 	psAbort.c \
Index: /branches/cnb_branches/cnb_branch_20090301/psLib/src/sys/psConfigure.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psLib/src/sys/psConfigure.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/psLib/src/sys/psConfigure.c	(revision 23352)
@@ -12,9 +12,7 @@
  *  @author Ross Harman, MHPCC
  *  @author Robert DeSonia, MHPCC
+ *  @author Paul Price, IfA
  *
- *  @version $Revision: 1.28 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2008-08-08 18:05:08 $
- *
- *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ *  Copyright 2004-2009 Institute for Astronomy, University of Hawaii
  */
 
@@ -26,4 +24,12 @@
 #include <stdlib.h>
 #include <string.h>
+
+#include <fitsio.h>
+#include <longnam.h>
+#include <fftw3.h>
+#include <gsl/gsl_version.h>
+#ifdef HAVE_PSDB
+#include <mysql.h>
+#endif
 
 #include "psAbort.h"
@@ -41,26 +47,69 @@
 static FILE *memCheckFile = NULL;       // File to which to write results of mem check
 
-static const char *cvsTag = "$Name: not supported by cvs2svn $"; // CVS tag name
+#ifndef PSLIB_VERSION
+#error "PSLIB_VERSION is not set"
+#endif
+#ifndef PSLIB_BRANCH
+#error "PSLIB_BRANCH is not set"
+#endif
+#ifndef PSLIB_SOURCE
+#error "PSLIB_SOURCE is not set"
+#endif
+
+#define xstr(s) str(s)
+#define str(s) #s
 
 psString psLibVersion(void)
 {
-    psString version = NULL;            // Version, to return
-    psStringAppend(&version, "%s-%s",PACKAGE_NAME,PACKAGE_VERSION);
-    return version;
+    char *value = NULL;
+    psStringAppend(&value, "%s@%s", xstr(PSLIB_BRANCH), xstr(PSLIB_VERSION));
+    return value;
+}
+
+psString psLibSource(void)
+{
+    return psStringCopy(xstr(PSLIB_SOURCE));
+}
+
+psString psLibDependencies(void)
+{
+    psString deps = NULL;               // Dependencies, to return
+    float cfitsioVersion;               // CFITSIO version number
+    psStringAppend(&deps, "cfitsio-%.3f gsl-%s %s",
+                   fits_get_version(&cfitsioVersion), gsl_version, fftwf_version);
+
+#ifdef HAVE_PSDB
+    psStringAppend(&deps, " mysql-%s", mysql_get_client_info());
+#endif
+
+    return deps;
 }
 
 psString psLibVersionLong(void)
 {
-    psString version = psLibVersion();    // Version, to return
-    psString tag = psStringStripCVS(cvsTag, "Name"); // CVS tag
+    psString version = psLibVersion();  // Version, to return
+    psString source = psLibSource();    // Source
+    psString deps = psLibDependencies();// Dependencies
 
-#ifdef HAVE_PSDB
-    psStringAppend(&version, " (cvs tag %s), %s, %s with psDB", tag, __DATE__, __TIME__);
+    psStringPrepend(&version, "psLib ");
+    psStringAppend(&version, " from %s, built %s, %s with %s",
+                   source, __DATE__, __TIME__, deps);
+    psFree(source);
+    psFree(deps);
+
+#ifdef __OPTIMIZE__
+    psStringAppend(&version, " optimised");
 #else
-    psStringAppend(&version, " (cvs tag %s), %s, %s without psDB", tag, __DATE__, __TIME__);
+    psStringAppend(&version, " unoptimised");
 #endif
-    psFree(tag);
+
+#ifdef PS_NO_TRACE
+    psStringAppend(&version, " without trace");
+#else
+    psStringAppend(&version, " with trace");
+#endif
+
     return version;
-}
+};
 
 // Check the memory; intended for use on exit, but might be used elsewhere
Index: /branches/cnb_branches/cnb_branch_20090301/psLib/src/sys/psConfigure.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psLib/src/sys/psConfigure.h	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/psLib/src/sys/psConfigure.h	(revision 23352)
@@ -20,4 +20,6 @@
 #define PS_CONFIGURE_H
 
+#include <psString.h>
+
 /// @addtogroup SysUtils System Utilities
 /// @{
@@ -29,8 +31,21 @@
  *  @return psString: String with version name.
  */
-psString psLibVersion(
-    void
-);
+psString psLibVersion(void);
 
+/** Get current psLib source
+ *
+ * Returns the current psLib source name as a string.
+ *
+ * @return psString: String with source name.
+ */
+psString psLibSource(void);
+
+/** Get psLib dependencies' versions
+ *
+ * Returns the psLib dependency versions as a string.
+ *
+ * @return psString: String with dependencies.
+ */
+psString psLibDependencies(void);
 
 /** Get current psLib version (full identification)
Index: /branches/cnb_branches/cnb_branch_20090301/psLib/src/sys/psLogMsg.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psLib/src/sys/psLogMsg.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/psLib/src/sys/psLogMsg.c	(revision 23352)
@@ -195,5 +195,5 @@
     }
 
-    int fileD = creat(dest, 0666);
+    int fileD = open(dest, O_WRONLY | O_CREAT, 0666);
     if (fileD == 0) {
         psError(PS_ERR_IO, true, _("Could not open file '%s' for output."), dest);
@@ -316,5 +316,5 @@
 
     if (write(logFD, head, strlen(head))) {;} // ignore return value
-    
+
     if (logMsg) {
         psString msg = NULL;            // Message to print
Index: /branches/cnb_branches/cnb_branch_20090301/psLib/src/sys/psMemory.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psLib/src/sys/psMemory.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/psLib/src/sys/psMemory.c	(revision 23352)
@@ -1069,2 +1069,17 @@
                    memBlock->func, memBlock->file, memBlock->lineno, (unsigned long)memBlock->tid);
 }
+
+bool psMemTypeEqual (void *ptr1, void *ptr2) {
+
+    // if ptr is a psAlloc()'d memory, find the actual address of the memBlock
+    psMemBlock *memBlock1 = ((psMemBlock *)ptr1) - 1;
+    HANDLE_BAD_BLOCK(memBlock1, __FILE__, __LINE__, __func__);
+    if (!memBlock1->freeFunc) return false;
+
+    // if ptr is a psAlloc()'d memory, find the actual address of the memBlock
+    psMemBlock *memBlock2 = ((psMemBlock *)ptr2) - 1;
+    HANDLE_BAD_BLOCK(memBlock2, __FILE__, __LINE__, __func__);
+    if (!memBlock2->freeFunc) return false;
+
+    return (memBlock1->freeFunc == memBlock2->freeFunc);
+}
Index: /branches/cnb_branches/cnb_branch_20090301/psLib/src/sys/psMemory.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psLib/src/sys/psMemory.h	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/psLib/src/sys/psMemory.h	(revision 23352)
@@ -636,4 +636,14 @@
 );
 
+/** test for matching types (equal free functions)
+ *
+ * This function returns true if the two pointers have matching, non-NULL free functions.
+ * Supplied pointers must have been allocated within the psLib memory system (ie, with a psAlloc) or the function will abort. (XXX just return false?)
+ * Supplied pointers must have been provided with free function or the function returns false.
+ */
+bool psMemTypeEqual (void *ptr1, ///< pointer to first psMemory object
+		     void *ptr2 ///< pointer to second psMemory object
+  );
+
 // Ensure this is a psLib pointer
 #define PS_ASSERT_PTR_HEAVY(PTR, RVAL) \
Index: /branches/cnb_branches/cnb_branch_20090301/psLib/src/types/Makefile.am
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psLib/src/types/Makefile.am	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/psLib/src/types/Makefile.am	(revision 23352)
@@ -14,4 +14,5 @@
 	psMetadataItemParse.c \
 	psMetadataItemCompare.c \
+	psMetadataHeader.c \
 	psPixels.c \
 	psArguments.c	\
@@ -30,4 +31,5 @@
 	psMetadataItemParse.h \
 	psMetadataItemCompare.h \
+	psMetadataHeader.h \
 	psPixels.h \
 	psArguments.h	\
Index: /branches/cnb_branches/cnb_branch_20090301/psLib/src/types/psMetadata.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psLib/src/types/psMetadata.h	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/psLib/src/types/psMetadata.h	(revision 23352)
@@ -30,4 +30,5 @@
 #include "psLookupTable.h"
 #include "psMutex.h"
+#include "psError.h"
 
 #define PS_DATA_IS_PRIMITIVE(TYPE) \
Index: /branches/cnb_branches/cnb_branch_20090301/psLib/src/types/psMetadataConfig.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psLib/src/types/psMetadataConfig.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/psLib/src/types/psMetadataConfig.c	(revision 23352)
@@ -1477,6 +1477,13 @@
             }
 
-            psString newStr = psMetadataConfigFormat(item->data.md);
-            if (newStr) {
+            psMetadata *md = item->data.md; // Metadata at new level
+            if (md) {
+                psString newStr = psMetadataConfigFormat(item->data.md);
+                if (!newStr) {
+                    psError(PS_ERR_UNKNOWN, false, "Unable to format metadata %s", item->name);
+                    psFree(content);
+                    return NULL;
+                }
+
                 // add 3 extra spaces to each metadata folder item
                 char *buf = strtok(newStr, "\n");
@@ -1487,5 +1494,4 @@
                 psFree(newStr);
             }
-
             psStringAppend(&content, "\nEND\n");
             break;
Index: /branches/cnb_branches/cnb_branch_20090301/psLib/src/types/psMetadataHeader.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psLib/src/types/psMetadataHeader.c	(revision 23352)
+++ /branches/cnb_branches/cnb_branch_20090301/psLib/src/types/psMetadataHeader.c	(revision 23352)
@@ -0,0 +1,30 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+
+#include "psMemory.h"
+#include "psConfigure.h"
+#include "psMetadata.h"
+
+#include "psMetadataHeader.h"
+
+bool psLibVersionHeader(psMetadata *header)
+{
+    PS_ASSERT_METADATA_NON_NULL(header, false);
+
+    psString version = psLibVersion();  // Software version
+    psString source = psLibSource();    // Software source
+
+    psStringPrepend(&version, "psLib version: ");
+    psStringPrepend(&source, "psLib source: ");
+
+    psMetadataAddStr(header, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, NULL, version);
+    psMetadataAddStr(header, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, NULL, source);
+
+    psFree(version);
+    psFree(source);
+
+    return true;
+}
Index: /branches/cnb_branches/cnb_branch_20090301/psLib/src/types/psMetadataHeader.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psLib/src/types/psMetadataHeader.h	(revision 23352)
+++ /branches/cnb_branches/cnb_branch_20090301/psLib/src/types/psMetadataHeader.h	(revision 23352)
@@ -0,0 +1,12 @@
+#ifndef PS_METADATA_HEADER_H
+#define PS_METADATA_HEADER_H
+
+#include <psMetadata.h>
+
+/// Populate a header with version information
+bool psLibVersionHeader(
+    psMetadata *header                  ///< Header to populate
+    );
+
+
+#endif
Index: /branches/cnb_branches/cnb_branch_20090301/psLib/test/fits/tap_psFitsImage.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psLib/test/fits/tap_psFitsImage.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/psLib/test/fits/tap_psFitsImage.c	(revision 23352)
@@ -12,5 +12,5 @@
 static psImage *generateImage(void)
 {
-    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 12345); // Random number generator
+    psRandom *rng = psRandomAllocSpecific(PS_RANDOM_TAUS, 12345); // Random number generator
     psImage *image = psImageAlloc(NUMCOLS, NUMROWS, PS_TYPE_F32); // Generated image
     for (int y = 0; y < NUMROWS; y++) {
Index: /branches/cnb_branches/cnb_branch_20090301/psLib/test/imageops/convolutionBench.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psLib/test/imageops/convolutionBench.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/psLib/test/imageops/convolutionBench.c	(revision 23352)
@@ -68,5 +68,5 @@
 int main(int argc, char *argv[])
 {
-    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 0); // Random number generator
+    psRandom *rng = psRandomAllocSpecific(PS_RANDOM_TAUS, 0); // Random number generator
 
     printf("#%14s%16s        %8s        %8s\n", "Image", "Kernel", "Direct", "FFT");
Index: /branches/cnb_branches/cnb_branch_20090301/psLib/test/imageops/tap_psImageInterpolate2.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psLib/test/imageops/tap_psImageInterpolate2.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/psLib/test/imageops/tap_psImageInterpolate2.c	(revision 23352)
@@ -77,5 +77,5 @@
     psImage *variance = NULL; // generateVariance(xSize, ySize, type);
     psImage *mask = NULL; // generateMask(xSize, ySize);
-    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 12345);
+    psRandom *rng = psRandomAllocSpecific(PS_RANDOM_TAUS, 12345);
 
     psImageInterpolation *interp = psImageInterpolationAlloc(mode, image, variance, mask, 0, NAN, NAN,
Index: /branches/cnb_branches/cnb_branch_20090301/psLib/test/math/tap_psMinimizeLMM.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psLib/test/math/tap_psMinimizeLMM.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/psLib/test/math/tap_psMinimizeLMM.c	(revision 23352)
@@ -95,5 +95,5 @@
     {
         psMemId id = psMemGetId();
-        psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 1); // Random number generator; using known seed
+        psRandom *rng = psRandomAllocSpecific(PS_RANDOM_TAUS, 1); // Random number generator; using known seed
         psMinimization *min = psMinimizationAlloc(NUM_ITERATIONS, ERR_TOL);
         psArray *ordinates = psArrayAlloc(NUM_DATA_POINTS);
@@ -203,5 +203,5 @@
     {
         psMemId id = psMemGetId();
-        psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 1); // Random number generator; using known seed
+        psRandom *rng = psRandomAllocSpecific(PS_RANDOM_TAUS, 1); // Random number generator; using known seed
         psMinimization *min = psMinimizationAlloc(NUM_ITERATIONS, ERR_TOL);
         psArray *ordinates = psArrayAlloc(NUM_DATA_POINTS);
Index: /branches/cnb_branches/cnb_branch_20090301/psLib/test/math/tap_psPolyFit1D.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psLib/test/math/tap_psPolyFit1D.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/psLib/test/math/tap_psPolyFit1D.c	(revision 23352)
@@ -109,5 +109,5 @@
     psVector *xTruth = psVectorAlloc(numData, PS_TYPE_F64);
     psVector *fTruth = psVectorAlloc(numData, PS_TYPE_F64);
-    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 1); // Using a known seed
+    psRandom *rng = psRandomAllocSpecific(PS_RANDOM_TAUS, 1); // Using a known seed
     for (int i = 0; i < numData; i++) {
         xTruth->data.F64[i] = (flags & TS00_X_NULL) ? i : 2.0*psRandomUniform(rng) - 1.0;
Index: /branches/cnb_branches/cnb_branch_20090301/psLib/test/math/tap_psPolyFit2D.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psLib/test/math/tap_psPolyFit2D.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/psLib/test/math/tap_psPolyFit2D.c	(revision 23352)
@@ -108,5 +108,5 @@
     yTruth->n = numData;
     fTruth->n = numData;
-    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 1); // Using an RNG with a known seed
+    psRandom *rng = psRandomAllocSpecific(PS_RANDOM_TAUS, 1); // Using an RNG with a known seed
     for (int i = 0; i < numData; i++) {
         xTruth->data.F64[i] = 2.0*psRandomUniform(rng) - 1.0;
Index: /branches/cnb_branches/cnb_branch_20090301/psLib/test/math/tap_psPolyFit3D.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psLib/test/math/tap_psPolyFit3D.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/psLib/test/math/tap_psPolyFit3D.c	(revision 23352)
@@ -108,5 +108,5 @@
     zTruth->n = numData;
     fTruth->n = numData;
-    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 1); // Using known seed
+    psRandom *rng = psRandomAllocSpecific(PS_RANDOM_TAUS, 1); // Using known seed
     for (int i = 0; i < numData; i++) {
         xTruth->data.F64[i] = 2.0*psRandomUniform(rng) - 1.0;
Index: /branches/cnb_branches/cnb_branch_20090301/psLib/test/math/tap_psPolyFit4D.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psLib/test/math/tap_psPolyFit4D.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/psLib/test/math/tap_psPolyFit4D.c	(revision 23352)
@@ -132,5 +132,5 @@
     tTruth->n = numData;
     fTruth->n = numData;
-    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 1); // Using known seed
+    psRandom *rng = psRandomAllocSpecific(PS_RANDOM_TAUS, 1); // Using known seed
     for (int i = 0; i < numData; i++) {
         xTruth->data.F64[i] = 2.0*psRandomUniform(rng) - 1.0;
Index: /branches/cnb_branches/cnb_branch_20090301/psLib/test/math/tap_psRandom.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psLib/test/math/tap_psRandom.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/psLib/test/math/tap_psRandom.c	(revision 23352)
@@ -3,5 +3,5 @@
 work properly.
  
-    ensure that psRandom structs are properly allocated by psRandomAlloc().
+    ensure that psRandom structs are properly allocated by psRandomAllocSpecific().
     ensure that psRandomUniform() produces a sequence of numbers with
         proper mean and stdev.
@@ -47,12 +47,12 @@
     plan_tests(34);
 
-    // ensure that psRandom structs are properly allocated by psRandomAlloc()
+    // ensure that psRandom structs are properly allocated by psRandomAllocSpecific()
     {
         psMemId id = psMemGetId();
         // Valid type allocation
-        psRandom *myRNG = psRandomAlloc(PS_RANDOM_TAUS, SEED);
-        ok(myRNG != NULL, "psRandom struct was allocated properly");
-        skip_start(myRNG == NULL, 1, "Skipping tests because psRandomAlloc() failed");
-        ok(myRNG->type == PS_RANDOM_TAUS, "psRandomAlloc() set type properly");
+        psRandom *myRNG = psRandomAllocSpecific(PS_RANDOM_TAUS, SEED);
+        ok(myRNG != NULL, "psRandom struct was allocated properly");
+        skip_start(myRNG == NULL, 1, "Skipping tests because psRandomAllocSpecific() failed");
+        ok(myRNG->type == PS_RANDOM_TAUS, "psRandomAllocSpecific() set type properly");
         psFree(myRNG);
         skip_end();
@@ -63,6 +63,6 @@
     {
         psMemId id = psMemGetId();
-        psRandom *myRNG = psRandomAlloc(100,SEED);
-        ok(myRNG == NULL, "psRandomAlloc() refused to generate psRandom with unallowed type");
+        psRandom *myRNG = psRandomAllocSpecific(100,SEED);
+        ok(myRNG == NULL, "psRandomAllocSpecific() refused to generate psRandom with unallowed type");
         psFree(myRNG);
         ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
@@ -72,6 +72,6 @@
     {
         psMemId id = psMemGetId();
-        psRandom *myRNG = psRandomAlloc(PS_RANDOM_TAUS,-5);
-        ok(myRNG != NULL, "psRandomAlloc() allows negative seed");
+        psRandom *myRNG = psRandomAllocSpecific(PS_RANDOM_TAUS,-5);
+        ok(myRNG != NULL, "psRandomAllocSpecific() allows negative seed");
         psFree(myRNG);
         ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
@@ -85,7 +85,7 @@
         rans->n = rans->nalloc;
         psStats *stats = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
-        psRandom *myRNG = psRandomAlloc(PS_RANDOM_TAUS, SEED);
-        ok(myRNG != NULL, "psRandom struct was allocated properly");
-        skip_start(myRNG == NULL, 2, "Skipping tests because psRandomAlloc() failed");
+        psRandom *myRNG = psRandomAllocSpecific(PS_RANDOM_TAUS, SEED);
+        ok(myRNG != NULL, "psRandom struct was allocated properly");
+        skip_start(myRNG == NULL, 2, "Skipping tests because psRandomAllocSpecific() failed");
 
         // Initialize vector data with random number
@@ -125,7 +125,7 @@
         rans->n = rans->nalloc;
         psStats *stats = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
-        psRandom *myRNG = psRandomAlloc(PS_RANDOM_TAUS, SEED);
-        ok(myRNG != NULL, "psRandom struct was allocated properly");
-        skip_start(myRNG == NULL, 2, "Skipping tests because psRandomAlloc() failed");
+        psRandom *myRNG = psRandomAllocSpecific(PS_RANDOM_TAUS, SEED);
+        ok(myRNG != NULL, "psRandom struct was allocated properly");
+        skip_start(myRNG == NULL, 2, "Skipping tests because psRandomAllocSpecific() failed");
 
         // Initialize vector with random data
@@ -167,7 +167,7 @@
         rans->n = rans->nalloc;
         psStats *stats = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
-        psRandom *myRNG = psRandomAlloc(PS_RANDOM_TAUS, SEED);
-        ok(myRNG != NULL, "psRandom struct was allocated properly");
-        skip_start(myRNG == NULL, 2, "Skipping tests because psRandomAlloc() failed");
+        psRandom *myRNG = psRandomAllocSpecific(PS_RANDOM_TAUS, SEED);
+        ok(myRNG != NULL, "psRandom struct was allocated properly");
+        skip_start(myRNG == NULL, 2, "Skipping tests because psRandomAllocSpecific() failed");
 
         // Initialize vector with random data
@@ -210,7 +210,7 @@
         rans02->n = rans02->nalloc;
 
-        psRandom *myRNG = psRandomAlloc(PS_RANDOM_TAUS, SEED);
-        ok(myRNG != NULL, "psRandom struct was allocated properly");
-        skip_start(myRNG == NULL, 1, "Skipping tests because psRandomAlloc() failed");
+        psRandom *myRNG = psRandomAllocSpecific(PS_RANDOM_TAUS, SEED);
+        ok(myRNG != NULL, "psRandom struct was allocated properly");
+        skip_start(myRNG == NULL, 1, "Skipping tests because psRandomAllocSpecific() failed");
 
 
@@ -252,5 +252,5 @@
     if (0) {
         psRandom *myRNG1 = NULL;
-        myRNG1 = psRandomAlloc(PS_RANDOM_TAUS, SEED);
+        myRNG1 = psRandomAllocSpecific(PS_RANDOM_TAUS, SEED);
         //    psLogSetDestination("dest:stderr");
         psLogSetDestination(0);
@@ -275,7 +275,7 @@
         rans02->n = rans02->nalloc;
 
-        psRandom *myRNG = psRandomAlloc(PS_RANDOM_TAUS, SEED);
-        ok(myRNG != NULL, "psRandom struct was allocated properly");
-        skip_start(myRNG == NULL, 1, "Skipping tests because psRandomAlloc() failed");
+        psRandom *myRNG = psRandomAllocSpecific(PS_RANDOM_TAUS, SEED);
+        ok(myRNG != NULL, "psRandom struct was allocated properly");
+        skip_start(myRNG == NULL, 1, "Skipping tests because psRandomAllocSpecific() failed");
 
 
@@ -325,7 +325,7 @@
         rans02->n = rans02->nalloc;
 
-        psRandom *myRNG = psRandomAlloc(PS_RANDOM_TAUS, SEED);
-        ok(myRNG != NULL, "psRandom struct was allocated properly");
-        skip_start(myRNG == NULL, 1, "Skipping tests because psRandomAlloc() failed");
+        psRandom *myRNG = psRandomAllocSpecific(PS_RANDOM_TAUS, SEED);
+        ok(myRNG != NULL, "psRandom struct was allocated properly");
+        skip_start(myRNG == NULL, 1, "Skipping tests because psRandomAllocSpecific() failed");
 
         // Initialize vectors with random data
Index: /branches/cnb_branches/cnb_branch_20090301/psLib/test/math/tap_psStats07.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psLib/test/math/tap_psStats07.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/psLib/test/math/tap_psStats07.c	(revision 23352)
@@ -58,6 +58,6 @@
     PS_ASSERT_INT_NONNEGATIVE(Npts, NULL);
 
-    //    psRandom *r = psRandomAlloc(PS_RANDOM_TAUS, p_psRandomGetSystemSeed());
-    psRandom *r = psRandomAlloc(PS_RANDOM_TAUS, PS_XXX_GAUSSIAN_SEED);
+    //    psRandom *r = psRandomAllocSpecific(PS_RANDOM_TAUS, p_psRandomGetSystemSeed());
+    psRandom *r = psRandomAllocSpecific(PS_RANDOM_TAUS, PS_XXX_GAUSSIAN_SEED);
     psVector* gauss = psVectorAlloc(Npts, PS_TYPE_F32);
     for (unsigned int i = 0; i < Npts; i++) {
Index: /branches/cnb_branches/cnb_branch_20090301/psLib/test/math/tap_psStats09.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psLib/test/math/tap_psStats09.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/psLib/test/math/tap_psStats09.c	(revision 23352)
@@ -52,5 +52,5 @@
     srand(SEED);
 
-    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 1); // Random number generator; using known seed
+    psRandom *rng = psRandomAllocSpecific(PS_RANDOM_TAUS, 1); // Random number generator; using known seed
     psVector *truth = psVectorAlloc(numData, PS_TYPE_F64);
     for (long i = 0; i < numData; i++) {
Index: /branches/cnb_branches/cnb_branch_20090301/psLib/test/math/tap_psStatsTiming.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psLib/test/math/tap_psStatsTiming.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/psLib/test/math/tap_psStatsTiming.c	(revision 23352)
@@ -20,5 +20,5 @@
 
     // build a gauss-deviate vector (mean = 0.0, sigma = 1.0) for tests
-    psRandom *seed = psRandomAlloc (PS_RANDOM_TAUS, 0);
+    psRandom *seed = psRandomAllocSpecific (PS_RANDOM_TAUS, 0);
     psVector *rnd = psVectorAlloc (1000, PS_TYPE_F32);
     for (int i = 0; i < rnd->n; i++) {
Index: /branches/cnb_branches/cnb_branch_20090301/psLib/test/math/tst_psMinimizeLMM.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psLib/test/math/tst_psMinimizeLMM.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/psLib/test/math/tst_psMinimizeLMM.c	(revision 23352)
@@ -66,5 +66,5 @@
     psBool testStatus = true;
 
-    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 1); // Random number generator; using known seed
+    psRandom *rng = psRandomAllocSpecific(PS_RANDOM_TAUS, 1); // Random number generator; using known seed
     psMinimization *min = psMinimizationAlloc(NUM_ITERATIONS, ERR_TOL);
     psArray *ordinates = psArrayAlloc(NUM_DATA_POINTS);
Index: /branches/cnb_branches/cnb_branch_20090301/psLib/test/math/tst_psPolyFit1D.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psLib/test/math/tst_psPolyFit1D.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/psLib/test/math/tst_psPolyFit1D.c	(revision 23352)
@@ -101,5 +101,5 @@
     xTruth->n = numData;
     fTruth->n = numData;
-    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 1); // Using a known seed
+    psRandom *rng = psRandomAllocSpecific(PS_RANDOM_TAUS, 1); // Using a known seed
     for (int i = 0; i < numData; i++) {
         xTruth->data.F64[i] = (flags & TS00_X_NULL) ? i : 2.0*psRandomUniform(rng) - 1.0;
Index: /branches/cnb_branches/cnb_branch_20090301/psLib/test/math/tst_psPolyFit2D.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psLib/test/math/tst_psPolyFit2D.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/psLib/test/math/tst_psPolyFit2D.c	(revision 23352)
@@ -100,5 +100,5 @@
     yTruth->n = numData;
     fTruth->n = numData;
-    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 1); // Using an RNG with a known seed
+    psRandom *rng = psRandomAllocSpecific(PS_RANDOM_TAUS, 1); // Using an RNG with a known seed
     for (int i = 0; i < numData; i++) {
         xTruth->data.F64[i] = 2.0*psRandomUniform(rng) - 1.0;
Index: /branches/cnb_branches/cnb_branch_20090301/psLib/test/math/tst_psPolyFit3D.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psLib/test/math/tst_psPolyFit3D.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/psLib/test/math/tst_psPolyFit3D.c	(revision 23352)
@@ -103,5 +103,5 @@
     zTruth->n = numData;
     fTruth->n = numData;
-    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 1); // Using known seed
+    psRandom *rng = psRandomAllocSpecific(PS_RANDOM_TAUS, 1); // Using known seed
     for (int i = 0; i < numData; i++) {
         xTruth->data.F64[i] = 2.0*psRandomUniform(rng) - 1.0;
Index: /branches/cnb_branches/cnb_branch_20090301/psLib/test/math/tst_psPolyFit4D.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psLib/test/math/tst_psPolyFit4D.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/psLib/test/math/tst_psPolyFit4D.c	(revision 23352)
@@ -124,5 +124,5 @@
     tTruth->n = numData;
     fTruth->n = numData;
-    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 1); // Using known seed
+    psRandom *rng = psRandomAllocSpecific(PS_RANDOM_TAUS, 1); // Using known seed
     for (int i = 0; i < numData; i++) {
         xTruth->data.F64[i] = 2.0*psRandomUniform(rng) - 1.0;
Index: /branches/cnb_branches/cnb_branch_20090301/psLib/test/math/tst_psRandom.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psLib/test/math/tst_psRandom.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/psLib/test/math/tst_psRandom.c	(revision 23352)
@@ -3,5 +3,5 @@
 work properly.
  
-    t00(): ensure that psRandom structs are properly allocated by psRandomAlloc().
+    t00(): ensure that psRandom structs are properly allocated by psRandomAllocSpecific().
     t01(): ensure that psRandomUniform() produces a sequence of numbers with
     proper mean and stdev.
@@ -45,5 +45,5 @@
 
 testDescription tests[] = {
-                              {testRandomAlloc,000,"psRandomAlloc",0,false},
+                              {testRandomAlloc,000,"psRandomAllocSpecific",0,false},
                               {testRandomUniform,000,"psRandomUniform",0,false},
                               {testRandomGaussian,000,"psRandomGaussian",0,false},
@@ -70,5 +70,5 @@
 
     // Valid type allocation
-    myRNG = psRandomAlloc(PS_RANDOM_TAUS, SEED);
+    myRNG = psRandomAllocSpecific(PS_RANDOM_TAUS, SEED);
     if (myRNG == NULL) {
         psError(PS_ERR_UNKNOWN,true,"Could not allocate psRandom structure");
@@ -85,5 +85,5 @@
     //    psLogSetDestination("file:seed_msglog1.txt");
     psLogSetDestination(fd1);
-    myRNG = psRandomAlloc(PS_RANDOM_TAUS, 0);
+    myRNG = psRandomAllocSpecific(PS_RANDOM_TAUS, 0);
     //    psLogSetDestination("dest:stderr");
     psLogSetDestination(2);
@@ -100,5 +100,5 @@
     // Invalid type allocation
     psLogMsg(__func__,PS_LOG_INFO,"Invalid type, should generate error message");
-    myRNG = psRandomAlloc(100,SEED);
+    myRNG = psRandomAllocSpecific(100,SEED);
     if (myRNG != NULL) {
         psError(PS_ERR_UNKNOWN,true,"Did not return NULL for invalid type");
@@ -107,5 +107,5 @@
 
     // Negative seed value
-    myRNG = psRandomAlloc(PS_RANDOM_TAUS,-5);
+    myRNG = psRandomAllocSpecific(PS_RANDOM_TAUS,-5);
     if(myRNG == NULL) {
         psError(PS_ERR_UNKNOWN,true,"Did not return allocated psRandom");
@@ -125,5 +125,5 @@
     psStats *stats = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
 
-    myRNG = psRandomAlloc(PS_RANDOM_TAUS, SEED);
+    myRNG = psRandomAllocSpecific(PS_RANDOM_TAUS, SEED);
     if (myRNG == NULL) {
         psError(PS_ERR_UNKNOWN,true,"Could not allocate psRandom structure");
@@ -171,5 +171,5 @@
     psStats *stats = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
 
-    myRNG = psRandomAlloc(PS_RANDOM_TAUS, SEED);
+    myRNG = psRandomAllocSpecific(PS_RANDOM_TAUS, SEED);
     if (myRNG == NULL) {
         psError(PS_ERR_UNKNOWN,true,"Could not allocate psRandom structure");
@@ -219,5 +219,5 @@
     psStats *stats = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
 
-    myRNG = psRandomAlloc(PS_RANDOM_TAUS, SEED);
+    myRNG = psRandomAllocSpecific(PS_RANDOM_TAUS, SEED);
     if (myRNG == NULL) {
         psError(PS_ERR_UNKNOWN,true,"Could not allocate psRandom structure");
@@ -268,5 +268,5 @@
     rans02->n = rans02->nalloc;
 
-    myRNG = psRandomAlloc(PS_RANDOM_TAUS, SEED);
+    myRNG = psRandomAllocSpecific(PS_RANDOM_TAUS, SEED);
     if (myRNG == NULL) {
         psError(PS_ERR_UNKNOWN,true,"Could not allocate psRandom structure");
@@ -302,5 +302,5 @@
 
     psRandom *myRNG1 = NULL;
-    myRNG1 = psRandomAlloc(PS_RANDOM_TAUS, SEED);
+    myRNG1 = psRandomAllocSpecific(PS_RANDOM_TAUS, SEED);
     //    psLogSetDestination("dest:stderr");
     psLogSetDestination(0);
@@ -326,5 +326,5 @@
     rans02->n = rans02->nalloc;
 
-    myRNG = psRandomAlloc(PS_RANDOM_TAUS, SEED);
+    myRNG = psRandomAllocSpecific(PS_RANDOM_TAUS, SEED);
     if (myRNG == NULL) {
         psError(PS_ERR_UNKNOWN,true,"Could not allocate psRandom structure");
@@ -372,5 +372,5 @@
     rans02->n = rans02->nalloc;
 
-    myRNG = psRandomAlloc(PS_RANDOM_TAUS, SEED);
+    myRNG = psRandomAllocSpecific(PS_RANDOM_TAUS, SEED);
     if (myRNG == NULL) {
         psError(PS_ERR_UNKNOWN,true,"Could not allocate psRandom structure");
Index: /branches/cnb_branches/cnb_branch_20090301/psLib/test/math/tst_psStats09.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psLib/test/math/tst_psStats09.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/psLib/test/math/tst_psStats09.c	(revision 23352)
@@ -52,5 +52,5 @@
     printPositiveTestHeader(stdout, "psMathUtils functions", "psVectorStats Clipped Stats Routine");
 
-    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 1); // Random number generator; using known seed
+    psRandom *rng = psRandomAllocSpecific(PS_RANDOM_TAUS, 1); // Random number generator; using known seed
     psVector *truth = psVectorAlloc(numData, PS_TYPE_F64);
     truth->n = numData;
Index: /branches/cnb_branches/cnb_branch_20090301/psLib/test/mathtypes/tap_psVectorSelect.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psLib/test/mathtypes/tap_psVectorSelect.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/psLib/test/mathtypes/tap_psVectorSelect.c	(revision 23352)
@@ -17,5 +17,5 @@
     psVector *vector = psVectorAlloc(size, PS_TYPE_F32);
     psVectorInit(vector, 0.0);
-    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 12345);
+    psRandom *rng = psRandomAllocSpecific(PS_RANDOM_TAUS, 12345);
     for (long i = 0; i < numNonzero; i++) {
         long index = psRandomUniform(rng) * size;
@@ -52,5 +52,5 @@
     psVector *vector = psVectorAlloc(size, PS_TYPE_F32);
     psVectorInit(vector, 0.0);
-    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 12345);
+    psRandom *rng = psRandomAllocSpecific(PS_RANDOM_TAUS, 12345);
     for (long i = 0; i < numNonzero; i++) {
         long index = psRandomUniform(rng) * size;
Index: /branches/cnb_branches/cnb_branch_20090301/psLib/test/types/tap_psTree.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psLib/test/types/tap_psTree.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/psLib/test/types/tap_psTree.c	(revision 23352)
@@ -16,5 +16,5 @@
         psVector *y = psVectorAlloc(NUM, PS_TYPE_F64);
 
-        psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 0);
+        psRandom *rng = psRandomAllocSpecific(PS_RANDOM_TAUS, 0);
         for (int i = 0; i < NUM; i++) {
             x->data.F64[i] = 2.0 * psRandomUniform(rng) - 1.0;
Index: /branches/cnb_branches/cnb_branch_20090301/psastro/src/Makefile.am
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psastro/src/Makefile.am	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/psastro/src/Makefile.am	(revision 23352)
@@ -1,6 +1,12 @@
+lib_LTLIBRARIES = libpsastro.la
 
-lib_LTLIBRARIES = libpsastro.la
-libpsastro_la_CFLAGS = $(PSASTRO_CFLAGS) $(PSMODULE_CFLAGS) $(PSLIB_CFLAGS)
-libpsastro_la_LDFLAGS = $(PSASTRO_LIBS) $(PSMODULE_LIBS) $(PSLIB_LIBS)
+libpsastro_la_CFLAGS = $(PSASTRO_CFLAGS) $(PPSTATS_CFLAGS) $(PSMODULE_CFLAGS) $(PSLIB_CFLAGS) -DPSASTRO_VERSION=$(SVN_VERSION) -DPSASTRO_BRANCH=$(SVN_BRANCH) -DPSASTRO_SOURCE=$(SVN_SOURCE)
+libpsastro_la_LDFLAGS = $(PSASTRO_LIBS) $(PPSTATS_LIBS) $(PSMODULE_LIBS) $(PSLIB_LIBS)
+
+# Force recompilation of psastroVersion.c, since it gets the version information
+# can we do this with dependency info?
+# psastroVersion.c: FORCE
+# touch psastroVersion.c
+# FORCE: ;
 
 bin_PROGRAMS = psastro psastroModel psastroModelFit gpcModel
Index: /branches/cnb_branches/cnb_branch_20090301/psastro/src/psastro.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psastro/src/psastro.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/psastro/src/psastro.c	(revision 23352)
@@ -34,8 +34,10 @@
     if (!config) usage ();
 
+    psastroVersionPrint();
+
     // load identify the data sources
     if (!psastroParseCamera (config)) {
-	psErrorStackPrint(stderr, "error setting up the camera\n");
-	exit (1);
+        psErrorStackPrint(stderr, "error setting up the camera\n");
+        exit (1);
     }
 
@@ -43,18 +45,18 @@
     // select subset of stars for astrometry
     if (!psastroDataLoad (config)) {
-	psErrorStackPrint(stderr, "error loading input data\n");
-	exit (1);
+        psErrorStackPrint(stderr, "error loading input data\n");
+        exit (1);
     }
 
     // run the full astrometry analysis (chip and/or mosaic)
     if (!psastroAnalysis (config)) {
-	psErrorStackPrint(stderr, "failure in psastro analysis\n");
-	exit (1);
+        psErrorStackPrint(stderr, "failure in psastro analysis\n");
+        exit (1);
     }
-    
+
     // write out the results
     if (!psastroDataSave (config)) {
-	psErrorStackPrint(stderr, "failed to write out data\n");
-	exit (1);
+        psErrorStackPrint(stderr, "failed to write out data\n");
+        exit (1);
     }
 
Index: /branches/cnb_branches/cnb_branch_20090301/psastro/src/psastro.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psastro/src/psastro.h	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/psastro/src/psastro.h	(revision 23352)
@@ -95,5 +95,9 @@
 // Return version strings.
 psString          psastroVersion(void);
+psString          psastroSource(void);
 psString          psastroVersionLong(void);
+bool              psastroVersionHeader(psMetadata *header);
+bool              psastroVersionHeaderFull(psMetadata *header);
+void              psastroVersionPrint(void);
 
 // demo plots
Index: /branches/cnb_branches/cnb_branch_20090301/psastro/src/psastroArguments.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psastro/src/psastroArguments.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/psastro/src/psastroArguments.c	(revision 23352)
@@ -6,6 +6,6 @@
  *
  *  @author IfA
- *  @version $Revision: 1.34 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2009-02-09 21:25:34 $
+ *  @version $Revision: 1.34.2.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2009-02-19 17:59:50 $
  *  Copyright 2009 Institute for Astronomy, University of Hawaii
  */
@@ -100,5 +100,5 @@
     if ((N = psArgumentGet (argc, argv, "-visual"))) {
         psArgumentRemove (N, &argc, argv);
-        pmAstromSetVisual (true);
+        pmVisualSetVisual (true);
     }
 
Index: /branches/cnb_branches/cnb_branch_20090301/psastro/src/psastroCleanup.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psastro/src/psastroCleanup.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/psastro/src/psastroCleanup.c	(revision 23352)
@@ -6,6 +6,6 @@
  *
  *  @author IfA
- *  @version $Revision: 1.8 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2009-02-09 21:25:34 $
+ *  @version $Revision: 1.8.2.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2009-02-19 17:59:50 $
  *  Copyright 2009 Institute for Astronomy, University of Hawaii
  */
@@ -16,5 +16,5 @@
 
     psFree (config);
-    pmAstromVisualClose ();
+    pmVisualClose ();
 
     psTimerStop ();
Index: /branches/cnb_branches/cnb_branch_20090301/psastro/src/psastroConvert.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psastro/src/psastroConvert.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/psastro/src/psastroConvert.c	(revision 23352)
@@ -99,9 +99,12 @@
 	int n = index->data.S32[i];
 	pmSource *source = sources->data[n];
-	// XXX this needs to be flexible
+
+        psTrace ("psastro", 6, "mag: %f +/- %f, mode: %x, skip: %x\n", source->psfMag, source->errMag, source->mode, skip);
+
 	if (source->mode & skip) { 
 	  nModeSkip ++;
 	  continue;
 	}
+
 	if ((iMagMax != 0.0) && (source->psfMag > iMagMax)) {
 	  nFaintSkip ++;
Index: /branches/cnb_branches/cnb_branch_20090301/psastro/src/psastroDataSave.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psastro/src/psastroDataSave.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/psastro/src/psastroDataSave.c	(revision 23352)
@@ -18,5 +18,5 @@
   return false; \
 }
-  
+
 /**
  * this loop saves the photometry/astrometry data files
@@ -31,6 +31,6 @@
     psMetadata *recipe  = psMetadataLookupPtr (NULL, config->recipes, PSASTRO_RECIPE);
     if (!recipe) {
-	psError(PSASTRO_ERR_CONFIG, true, "Can't find PSASTRO recipe!\n");
-	return false;
+        psError(PSASTRO_ERR_CONFIG, true, "Can't find PSASTRO recipe!\n");
+        return false;
     }
 
@@ -38,6 +38,6 @@
     pmFPAfile *output = psMetadataLookupPtr (NULL, config->files, "PSASTRO.OUTPUT");
     if (!output) {
-	psError(PSASTRO_ERR_CONFIG, true, "Can't find or interpret output file rule PSASTRO.OUTPUT!\n");
-	return false;
+        psError(PSASTRO_ERR_CONFIG, true, "Can't find or interpret output file rule PSASTRO.OUTPUT!\n");
+        return false;
     }
 
@@ -48,4 +48,5 @@
 
     pmFPAview *view = pmFPAviewAlloc (0);
+    pmHDU *lastHDU = NULL;              // Last HDU updated
 
     // open/load files as needed
@@ -55,21 +56,28 @@
         psTrace ("psastro", 4, "Chip %d: %x %x\n", view->chip, chip->file_exists, chip->process);
         if (!chip->process || !chip->file_exists) { continue; }
-	if (!pmFPAfileIOChecks (config, view, PM_FPA_BEFORE)) ESCAPE;
+        if (!pmFPAfileIOChecks (config, view, PM_FPA_BEFORE)) ESCAPE;
 
-	while ((cell = pmFPAviewNextCell (view, output->fpa, 1)) != NULL) {
+        while ((cell = pmFPAviewNextCell (view, output->fpa, 1)) != NULL) {
             psTrace ("psastro", 4, "Cell %d: %x %x\n", view->cell, cell->file_exists, cell->process);
             if (!cell->process || !cell->file_exists) { continue; }
-	    if (!pmFPAfileIOChecks (config, view, PM_FPA_BEFORE)) ESCAPE;
+            if (!pmFPAfileIOChecks (config, view, PM_FPA_BEFORE)) ESCAPE;
 
-	    // process each of the readouts
-	    while ((readout = pmFPAviewNextReadout (view, output->fpa, 1)) != NULL) {
-		if (!pmFPAfileIOChecks (config, view, PM_FPA_BEFORE)) ESCAPE;
-		if (!readout->data_exists) { continue; }
+            // process each of the readouts
+            while ((readout = pmFPAviewNextReadout (view, output->fpa, 1)) != NULL) {
+                if (!pmFPAfileIOChecks (config, view, PM_FPA_BEFORE)) ESCAPE;
+                if (!readout->data_exists) { continue; }
 
-		if (!pmFPAfileIOChecks (config, view, PM_FPA_AFTER)) ESCAPE;
-	    }
-	    if (!pmFPAfileIOChecks (config, view, PM_FPA_AFTER)) ESCAPE;
-	}
-	if (!pmFPAfileIOChecks (config, view, PM_FPA_AFTER)) ESCAPE;
+                // Put version information into the header
+                pmHDU *hdu = pmHDUGetHighest(output->fpa, chip, cell);
+                if (hdu && hdu != lastHDU) {
+                    psastroVersionHeaderFull(hdu->header);
+                    lastHDU = hdu;
+                }
+
+                if (!pmFPAfileIOChecks (config, view, PM_FPA_AFTER)) ESCAPE;
+            }
+            if (!pmFPAfileIOChecks (config, view, PM_FPA_AFTER)) ESCAPE;
+        }
+        if (!pmFPAfileIOChecks (config, view, PM_FPA_AFTER)) ESCAPE;
     }
     if (!pmFPAfileIOChecks (config, view, PM_FPA_AFTER)) ESCAPE;
Index: /branches/cnb_branches/cnb_branch_20090301/psastro/src/psastroVersion.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psastro/src/psastroVersion.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/psastro/src/psastroVersion.c	(revision 23352)
@@ -5,7 +5,4 @@
  *  @ingroup libpsastro
  *
- *  @author IfA
- *  @version $Revision: 1.3 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2009-02-07 02:03:34 $
  *  Copyright 2009 Institute for Astronomy, University of Hawaii
  */
@@ -13,20 +10,110 @@
 #include "psastroInternal.h"
 
-static const char *cvsTag = "$Name: not supported by cvs2svn $";///< CVS tag name
+#ifndef PSASTRO_VERSION
+#error "PSASTRO_VERSION is not set"
+#endif
+#ifndef PSASTRO_BRANCH
+#error "PSASTRO_BRANCH is not set"
+#endif
+#ifndef PSASTRO_SOURCE
+#error "PSASTRO_SOURCE is not set"
+#endif
+
+#define xstr(s) str(s)
+#define str(s) #s
 
 psString psastroVersion(void)
 {
-    psString version = NULL;            // Version, to return
-    psStringAppend(&version, "%s-%s",PACKAGE_NAME,PACKAGE_VERSION);
-    return version;
+    char *value = NULL;
+    psStringAppend(&value, "%s@%s", xstr(PSASTRO_BRANCH), xstr(PSASTRO_VERSION));
+    return value;
+}
+
+psString psastroSource(void)
+{
+  return psStringCopy(xstr(PSASTRO_SOURCE));
 }
 
 psString psastroVersionLong(void)
 {
-    psString version = psastroVersion(); // Version, to return
-    psString tag = psStringStripCVS(cvsTag, "Name"); // CVS tag
-    psStringAppend(&version, " (cvs tag %s) %s, %s", tag, __DATE__, __TIME__);
-    psFree(tag);
+    psString version = psastroVersion();  // Version, to return
+    psString source = psastroSource();    // Source
+
+    psStringPrepend(&version, "psastro ");
+    psStringAppend(&version, " from %s, built %s, %s", source, __DATE__, __TIME__);
+    psFree(source);
+
+#ifdef __OPTIMIZE__
+    psStringAppend(&version, " optimised");
+#else
+    psStringAppend(&version, " unoptimised");
+#endif
+
     return version;
+};
+
+bool psastroVersionHeader(psMetadata *header)
+{
+    PS_ASSERT_METADATA_NON_NULL(header, false);
+
+    psString version = psastroVersion(); // Software version
+    psString source = psastroSource();   // Software source
+
+    psStringPrepend(&version, "psastro version: ");
+    psStringPrepend(&source, "psastro source: ");
+
+    psMetadataAddStr(header, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, NULL, version);
+    psMetadataAddStr(header, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, NULL, source);
+
+    psFree(version);
+    psFree(source);
+
+    return true;
 }
 
+bool psastroVersionHeaderFull(psMetadata *header)
+{
+    PS_ASSERT_METADATA_NON_NULL(header, false);
+
+    psTime *time = psTimeGetNow(PS_TIME_TAI); // The time now
+    psString timeString = psTimeToISO(time); // The time in an ISO string
+    psFree(time);
+    psString history = NULL;               // History string
+    psStringAppend(&history, "psastro at %s", timeString);
+    psFree(timeString);
+    psMetadataAddStr(header, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, NULL, history);
+    psFree(history);
+
+    psLibVersionHeader(header);
+    psModulesVersionHeader(header);
+    ppStatsVersionHeader(header);
+    psastroVersionHeader(header);
+
+    return true;
+}
+
+void psastroVersionPrint(void)
+{
+    psTime *time = psTimeGetNow(PS_TIME_TAI); // The time now
+    psString timeString = psTimeToISO(time); // The time in an ISO string
+    psFree(time);
+    psLogMsg("psastro", PS_LOG_INFO, "psastro at %s", timeString);
+    psFree(timeString);
+
+    psString pslib = psLibVersionLong();// psLib version
+    psString psmodules = psModulesVersionLong(); // psModules version
+    psString ppStats = ppStatsVersionLong(); // ppStats version
+    psString psastro = psastroVersionLong(); // psastro version
+
+    psLogMsg("psastro", PS_LOG_INFO, "%s", pslib);
+    psLogMsg("psastro", PS_LOG_INFO, "%s", psmodules);
+    psLogMsg("psastro", PS_LOG_INFO, "%s", ppStats);
+    psLogMsg("psastro", PS_LOG_INFO, "%s", psastro);
+
+    psFree(pslib);
+    psFree(psmodules);
+    psFree(ppStats);
+    psFree(psastro);
+
+    return;
+}
Index: anches/cnb_branches/cnb_branch_20090301/psastro/src/psastroVisual.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psastro/src/psastroVisual.c	(revision 23351)
+++ 	(revision )
@@ -1,1320 +1,0 @@
-/** @file psastroVisual.c
- *
- *  @brief Diagnostic plots for psastro
- *
- *  @ingroup libpsastro
- *
- *  @author IfA
- *  @version $Revision: 1.3 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2009-02-07 02:03:34 $
- *  Copyright 2009 Institute for Astronomy, University of Hawaii
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-# include "psastroInternal.h"
-# include <string.h>
-# include <stdlib.h>
-# include <stdio.h>
-
-# if (HAVE_KAPA)
-# include <kapa.h>
-
-# define KAPAX  700
-# define KAPAY  700
-
-//variables to determine when things are plotted
-static bool isVisual             = false;
-static bool plotRawStars         = true;
-static bool plotRefStars         = false;
-static bool plotLumFunc          = true;
-static bool plotRemoveClumps     = true;
-static bool plotOneChipFit       = true;
-static bool plotFixChips         = true;
-static bool plotAstromGuessCheck = true;
-static bool plotMosaicMatches    = true;
-static bool plotCommonScale      = true;
-static bool plotMosaicOneChip    = true;
-
-// variables to store plotting window indices
-static int kapa = -1;
-static int kapa2 = -1;
-
-// helper routine prototypes
-bool psastroVisualInitGraph (int kapa, KapaSection *section, Graphdata *graphdata);
-bool psastroVisualTriplePlot (int kapa, Graphdata *graphdata, psVector *xVec, psVector *yVec, psVector *zVec, bool increasing);
-bool psastroVisualScaleGraphdata(Graphdata *graphdata, psVector *xVec, psVector *yVec, bool clip);
-bool psastroVisualTripleOverplot (int kapa, Graphdata *graphdata, psVector *xVec, psVector *yVec, psVector *zVec, bool increasing);
-bool psastroVisualCreateScaleVec (psVector *zVec, psVector *zScale, bool increasing);
-bool psastroVisualResidPlot (psArray *rawstars, psArray *refstars, psArray *match, psMetadata *recipe, char *title);
-
-
-/*****************************/
-/** Initialization Routines **/
-/*****************************/
-
-/**  
- * start or stop plotting 
- */
-bool psastroSetVisual (bool mode) {
-    isVisual = mode;
-    return true;
-}
-
-
-/** 
- * open, name, and resize a window if necessary
- */
-bool psastroVisualInitWindow( int *kapid, char *name ) {
-    if (*kapid == -1) {
-        *kapid = KapaOpenNamedSocket("kapa", name);
-        if (*kapid == -1) {
-            fprintf (stderr, "failure to open kapa; visual mode disabled\n");
-            isVisual = false;
-            return false;
-        }
-        KapaResize (*kapid, KAPAX, KAPAY);
-    }
-    return true;
-}
-
-
-/** 
- * ask the user how to proceed
- */
-bool psastroVisualAskUser( bool *plotflag ) {
-    char key[10];
-    fprintf (stdout, "[c]ontinue? [s]kip the rest of these plots? [a]bort all visual plots?");
-    if (!fgets(key, 8, stdin)) {
-        psWarning("Unable to read option");
-    }
-    if (key[0] == 's') {
-        *plotflag = false;
-    }
-    if (key[0] == 'a') {
-        psastroSetVisual(false);
-        pmAstromSetVisual(false);
-    }
-    return true;
-}
-
-
-/** 
- * destroy windows at the end of a run
- */
-bool psastroVisualClose() {
-    if(kapa != -1)
-        KiiClose(kapa);
-    if (kapa2 != -1)
-        KiiClose(kapa2);
-    return true;
-}
-
-
-/***********************/
-/** Plotting Routines **/
-/***********************/
-
-
-/**
- * Plot raw stars as determined from first pass astrometry fit
- * Called within psastroAstromGeuss
- */
-bool psastroVisualPlotRawStars (psArray *rawstars, pmFPA *fpa, pmChip *chip, psMetadata *recipe)
-{
-    // make sure we want to plot this
-    if (!plotRawStars || !isVisual) return true;
-
-    //set up plot region
-    if (!psastroVisualInitWindow (&kapa, "psastro:plots"))
-        return false;
-    Graphdata graphdata;
-    KapaSection section;
-
-    KapaInitGraph (&graphdata);
-    KapaClearPlots (kapa);
-
-    graphdata.color = KapaColorByName ("black");
-    graphdata.ptype = 7;
-    graphdata.size = 0.5;
-    graphdata.style = 2;
-
-    section.dx = 0.4;
-    section.dy = 0.4;
-
-    //initialize and populate plotting vectors
-    bool status = false;
-    float iMagMin = psMetadataLookupF32 (&status, recipe, "PSASTRO.PLOT.INST.MAG.MIN");
-    float iMagMax = psMetadataLookupF32 (&status, recipe, "PSASTRO.PLOT.INST.MAG.MAX");
-
-    psVector *xVec = psVectorAlloc (rawstars->n, PS_TYPE_F32);
-    psVector *yVec = psVectorAlloc (rawstars->n, PS_TYPE_F32);
-    psVector *zVec = psVectorAlloc (rawstars->n, PS_TYPE_F32);
-
-    section.x = 0.0;
-    section.y = 0.5;
-    section.name = NULL;
-    psStringAppend (&section.name, "a0");
-    KapaSetSection (kapa, &section);
-    psFree (section.name);
-
-    //Chip coordinates
-    int n = 0;
-    for (int i = 0; i < rawstars->n; i++) {
-        pmAstromObj *raw = rawstars->data[i];
-        if (!isfinite(raw->Mag)) continue;
-        if (raw->Mag < iMagMin) continue;
-        if (raw->Mag > iMagMax) continue;
-
-        xVec->data.F32[n] = raw->chip->x;
-        yVec->data.F32[n] = raw->chip->y;
-        zVec->data.F32[n] = raw->Mag;
-        n++;
-    }
-    xVec->n = yVec->n = zVec->n = n;
-
-    KapaSendLabel(kapa, "Chip", KAPA_LABEL_XP);
-    KapaSendLabel(kapa, "X", KAPA_LABEL_XM);
-    KapaSendLabel(kapa, "Y", KAPA_LABEL_YM);
-    psastroVisualTriplePlot (kapa, &graphdata, xVec, yVec, zVec, false);
-
-    //Focal Plane Coordinates
-    section.x = 0.5;
-    section.y = 0.5;
-    section.name = NULL;
-    psStringAppend (&section.name, "a1");
-    KapaSetSection (kapa, &section);
-    psFree (section.name);
-
-    n = 0;
-    for (int i = 0; i < rawstars->n; i++) {
-        pmAstromObj *raw = rawstars->data[i];
-        if (!isfinite(raw->Mag)) continue;
-        if (raw->Mag < iMagMin) continue;
-        if (raw->Mag > iMagMax) continue;
-
-        xVec->data.F32[n] = raw->FP->x;
-        yVec->data.F32[n] = raw->FP->y;
-        zVec->data.F32[n] = raw->Mag;
-        n++;
-    }
-    xVec->n = yVec->n = zVec->n = n;
-
-    KapaSendLabel (kapa, "Focal Plane", KAPA_LABEL_XP);
-    KapaSendLabel (kapa, "L", KAPA_LABEL_XM);
-    KapaSendLabel (kapa, "M", KAPA_LABEL_YM);
-    psastroVisualTriplePlot (kapa, &graphdata, xVec, yVec, zVec, false);
-
-    // Tangent Plane Coordinates
-    section.x = 0.0;
-    section.y = 0.0;
-    section.name = NULL;
-    psStringAppend (&section.name, "a2");
-    KapaSetSection (kapa, &section);
-    psFree (section.name);
-
-    n = 0;
-    for (int i = 0; i < rawstars->n; i++) {
-        pmAstromObj *raw = rawstars->data[i];
-        if (!isfinite(raw->Mag)) continue;
-        if (raw->Mag < iMagMin) continue;
-        if (raw->Mag > iMagMax) continue;
-
-        xVec->data.F32[n] = raw->TP->x;
-        yVec->data.F32[n] = raw->TP->y;
-        zVec->data.F32[n] = raw->Mag;
-        n++;
-    }
-    xVec->n = yVec->n = zVec->n = n;
-
-    KapaSendLabel (kapa, "Tangential Plane", KAPA_LABEL_XP);
-    KapaSendLabel (kapa, "P", KAPA_LABEL_XM);
-    KapaSendLabel (kapa, "Q", KAPA_LABEL_YM);
-    psastroVisualTriplePlot (kapa, &graphdata, xVec, yVec, zVec, false);
-
-    //sky coordinates
-    section.x = 0.5;
-    section.y = 0.0;
-    section.name = NULL;
-    psStringAppend (&section.name, "a3");
-    KapaSetSection (kapa, &section);
-    psFree (section.name);
-
-    n = 0;
-    for (int i = 0; i < rawstars->n; i++) {
-        pmAstromObj *raw = rawstars->data[i];
-        if (!isfinite(raw->Mag)) continue;
-        if (raw->Mag < iMagMin) continue;
-        if (raw->Mag > iMagMax) continue;
-
-        xVec->data.F32[n] = DEG_RAD*raw->sky->r;
-        yVec->data.F32[n] = DEG_RAD*raw->sky->d;
-        zVec->data.F32[n] = raw->Mag;
-        n++;
-    }
-    xVec->n = yVec->n = zVec->n = n;
-
-    KapaSendLabel (kapa, "Sky", KAPA_LABEL_XP);
-    KapaSendLabel(kapa, "RA", KAPA_LABEL_XM);
-    KapaSendLabel(kapa, "Dec", KAPA_LABEL_YM);
-    psastroVisualTriplePlot (kapa, &graphdata, xVec, yVec, zVec, false);
-
-    // flip x (East increase to left)
-    SWAP (graphdata.xmin, graphdata.xmax);
-    KapaSetLimits (kapa, &graphdata);
-
-    // plot label
-    section.x = 0.0;
-    section.y = 0.0;
-    section.dx = .95;
-    section.dy = .95;
-    section.name = NULL;
-    psStringAppend (&section.name, "a5");
-    KapaSetSection (kapa, &section);
-    KapaSendLabel (kapa, "Raw Star Selection - Initial Astrometry", KAPA_LABEL_XP);
-    psFree (section.name);
-
-    // pause and wait for user input:
-    psastroVisualAskUser( &plotRawStars );
-
-    psFree (xVec);
-    psFree (yVec);
-    psFree (zVec);
-    return true;
-}
-
-
-/**
- * plot the location of references stars over the entire fpa
- * invoked during psastroChooseRefStars
- */
-bool psastroVisualPlotRefStars (psArray *refstars, psMetadata *recipe)
-{
-    //make sure we want to plot this
-    if (!isVisual || !plotRefStars) return true;
-
-    //set up plotting variables
-    if (!psastroVisualInitWindow (&kapa, "psastro:plots"))
-        return false;
-
-    Graphdata graphdata;
-    KapaInitGraph (&graphdata);
-    KapaClearSections (kapa);
-
-    graphdata.color = KapaColorByName ("black");
-    graphdata.ptype = 7;
-    graphdata.size = 0.5;
-    graphdata.style = 2;
-
-    //initialize and populate plot vectors
-    bool status = false;
-    float rMagMin = psMetadataLookupF32 (&status, recipe, "PSASTRO.PLOT.REF.MAG.MIN");
-    float rMagMax = psMetadataLookupF32 (&status, recipe, "PSASTRO.PLOT.REF.MAG.MAX");
-
-    psVector *xVec = psVectorAlloc (refstars->n, PS_TYPE_F32);
-    psVector *yVec = psVectorAlloc (refstars->n, PS_TYPE_F32);
-    psVector *zVec = psVectorAlloc (refstars->n, PS_TYPE_F32);
-
-    int n = 0;
-    for (int i = 0; i < refstars->n; i++) {
-        pmAstromObj *ref = refstars->data[i];
-        if (!isfinite(ref->Mag)) continue;
-        if (ref->Mag > rMagMax) continue;
-        if (ref->Mag < rMagMin) continue;
-
-        xVec->data.F32[n] = DEG_RAD*ref->sky->r;
-        yVec->data.F32[n] = DEG_RAD*ref->sky->d;
-        zVec->data.F32[n] = ref->Mag;
-        n++;
-    }
-    xVec->n = yVec->n = zVec->n = n;
-
-    KapaSendLabel (kapa, "RA", KAPA_LABEL_XM);
-    KapaSendLabel (kapa, "Dec", KAPA_LABEL_YM);
-    KapaSendLabel (kapa, "Reference Stars", KAPA_LABEL_XP);
-    psastroVisualTriplePlot(kapa, &graphdata, xVec, yVec, zVec, false);
-
-    // flip x (East increase to left)
-    SWAP (graphdata.xmin, graphdata.xmax);
-    KapaSetLimits (kapa, &graphdata);
-
-    // pause and wait for user input:
-    psastroVisualAskUser(&plotRefStars);
-
-    psFree (xVec);
-    psFree (yVec);
-    psFree (zVec);
-    return true;
-}
-
-
-/**
- * Plot the two luminosity functions created within psastroRefStarSubset
- * The luminosity functions are used to select a subset of reference stars,
- * so we plot the cutoff that defines this subset
- */
-bool psastroVisualPlotLuminosityFunction (psVector *lnMag,   ///< Log(n) for each magnitude bin
-                                          psVector *Mag,     ///< magnitude bins
-                                          pmLumFunc *lumFunc,///< Fit to the reference star luminosity function
-                                          pmLumFunc *rawFunc ///< Fit to the raw star luminoisty function
-                                          )
-{
-
-    // make sure we want to plot this
-    if ( !isVisual || !plotLumFunc ) return true;
-
-    //set up plotting variables
-    if ( !psastroVisualInitWindow (&kapa, "psastro:plots"))
-        return false;
-
-    Graphdata graphdata;
-    KapaSection section1 = {"s1", .1, .1, .85, .35};
-    KapaSection section2 =  {"s2", .1, .5, .85, .35};
-    KapaSection section;
-    if(rawFunc == NULL)
-        section = section1;
-    else
-        section = section2;
-    if (rawFunc == NULL)
-        KapaClearPlots(kapa);
-    KapaInitGraph(&graphdata);
-
-    //Determine Plot Limits
-    psastroVisualScaleGraphdata(&graphdata, Mag, lnMag, false);
-
-    //Make a line for the fit
-    float x[2] = {graphdata.xmin, graphdata.xmax};
-    float y[2] = {lumFunc->offset + x[0] * lumFunc->slope,
-                 lumFunc->offset + x[1] * lumFunc->slope};
-
-    //Plot Data
-    KapaSetSection(kapa, &section);
-    KapaSetLimits(kapa, &graphdata);
-
-    KapaSetFont (kapa, "helvetica", 14);
-    KapaBox (kapa, &graphdata);
-    KapaSendLabel (kapa, "Magnitude", KAPA_LABEL_XM);
-    KapaSendLabel (kapa, "Log(N)", KAPA_LABEL_YM);
-    if (rawFunc == NULL)
-        KapaSendLabel (kapa, "Raw Star Luminosity Function", KAPA_LABEL_XP);
-    else
-        KapaSendLabel (kapa,
-                       "Reference Star Luminosity Function, Shifted Raw Fit, and Cutoff",
-                       KAPA_LABEL_XP);
-    graphdata.color=KapaColorByName("black");
-    graphdata.style = 1;
-    KapaPrepPlot (kapa, lnMag->n, &graphdata);
-    KapaPlotVector(kapa, lnMag->n,   Mag->data.F32, "x");
-    KapaPlotVector(kapa, lnMag->n, lnMag->data.F32, "y");
-
-    //Overplot fit
-    graphdata.style=0;
-    KapaPrepPlot(kapa,2,&graphdata);
-    KapaPlotVector(kapa, 2, x, "x");
-    KapaPlotVector(kapa, 2, y, "y");
-
-    //If rawFunc was supplied, overplot the raw star fit + cutoff
-    if( rawFunc != NULL) {
-        double mRef = 0.5*(lumFunc->mMin + lumFunc->mMax);
-        double logRho = mRef * lumFunc->slope + lumFunc->offset;
-        double mRaw = (logRho - rawFunc->offset) / rawFunc->slope;
-        double deltaM = mRef - mRaw;
-        double mRefMax = rawFunc->mMax + deltaM;
-
-        float xraw[2] = {rawFunc->mMin + deltaM, rawFunc->mMax + deltaM};
-        float yraw[2] = {rawFunc->offset + (rawFunc->slope) * rawFunc->mMin,
-                        rawFunc->offset + (rawFunc->slope) * rawFunc->mMax};
-        float x[2] = {mRefMax, mRefMax};
-        float y[2] = {graphdata.ymin, graphdata.ymax};
-        graphdata.color= KapaColorByName("red");
-        KapaPrepPlot(kapa, 2, &graphdata);
-        KapaPlotVector(kapa, 2, x, "x");
-        KapaPlotVector(kapa, 2, y, "y");
-        KapaPrepPlot (kapa, 2, &graphdata);
-        KapaPlotVector (kapa, 2, xraw, "x");
-        KapaPlotVector (kapa, 2, yraw, "y");
-
-        // pause and wait for user input:
-        psastroVisualAskUser (&plotLumFunc);
-    }
-    return true;
-} // end of psastroVisualPlotLuminosityFunction
-
-
-/**
- * Plot the stars in a region, and indicate which stars are part of 'clumps'
- * These stars are flagged during astrometric fitting, since dense regions are
- * harder to cross-match than sparse ones. Called during psastroRemoveClumps.
- */
-bool psastroVisualPlotRemoveClumps (psArray *input, ///< Array containing the field stars
-                                    psImage *count, ///< A 2D histogram of the field star distribution
-                                    int scale,      ///< The pixel size of the histogram
-                                    float limit     ///< The minimum numuber of stars in a bin flagged as a clump
-                                    )
-{
-
-    //make sure we want to plot this
-    if (!isVisual || !plotRemoveClumps) return true;
-
-    //set up plot variables
-    if ( !psastroVisualInitWindow (&kapa, "psastro:plots")) return false;
-
-    KapaSection section;
-    Graphdata graphdata;
-    section.x = 0;
-    section.dx = .9;
-    section.y = 0;
-    section.dy = .9;
-    section.name = NULL;
-    psStringAppend( &section.name, "a0");
-    KapaInitGraph(&graphdata);
-    KapaSetSection(kapa, &section);
-    psFree(section.name);
-
-    graphdata.ptype = 7;
-    graphdata.size = 0.5;
-    graphdata.style = 2;
-    graphdata.color = KapaColorByName ("black");
-    KapaClearPlots(kapa);
-
-    //set up plot vectors
-    float Xmin = +FLT_MAX;
-    float Xmax = -FLT_MAX;
-    float Ymin = +FLT_MAX;
-    float Ymax = -FLT_MAX;
-    psVector *xVec = psVectorAlloc (input->n, PS_TYPE_F32);
-    psVector *yVec = psVectorAlloc (input->n, PS_TYPE_F32);
-
-    //determine boundaries for histogram bin calculation
-    int n = 0;
-    for (int i=0; i< input->n; i++) {
-        pmAstromObj *obj = (pmAstromObj *)input->data[i];
-        if (!isfinite(obj->FP->x)) continue;
-        if (!isfinite(obj->FP->y)) continue;
-        xVec->data.F32[n] = obj->FP->x;
-        yVec->data.F32[n] = obj->FP->y;
-        Xmin = PS_MIN (Xmin, xVec->data.F32[n]);
-        Xmax = PS_MAX (Xmax, xVec->data.F32[n]);
-        Ymin = PS_MIN (Ymin, yVec->data.F32[n]);
-        Ymax = PS_MAX (Ymax, yVec->data.F32[n]);
-        n++;
-    }
-    xVec->n = yVec->n = n;
-
-    //plot stars
-    graphdata.xmax = Xmax;
-    graphdata.xmin = Xmin;
-    graphdata.ymax = Ymax;
-    graphdata.ymin = Ymin;
-    KapaSetLimits (kapa, &graphdata);
-    KapaSetFont (kapa, "helvetica", 14);
-
-    KapaBox (kapa, &graphdata);
-
-    KapaSendLabel (kapa, "L (pixels)", KAPA_LABEL_XM);
-    KapaSendLabel (kapa, "M (pixels)", KAPA_LABEL_YM);
-    KapaSendLabel (kapa, "Regions Flagged as Clumps (Red Boxes)",
-                   KAPA_LABEL_XP);
-
-    KapaPrepPlot (kapa, xVec->n, &graphdata);
-    KapaPlotVector (kapa, xVec->n, xVec->data.F32, "x");
-    KapaPlotVector (kapa, yVec->n, yVec->data.F32, "y");
-
-    graphdata.color = KapaColorByName ("red");
-    graphdata.style = 1;
-
-    //overplot clumpy regions excluded from analysis
-    for(int i = 0; i < count->numCols; i++) {
-        for (int j = 0; j < count->numRows; j++) {
-            if(count->data.U32[j][i] <= limit) continue; //not a clump
-            float Xbot = (i - 5) * scale + Xmin;
-            float Ybot = (j - 5) * scale + Ymin;
-            if(Xbot < graphdata.xmin || Xbot > graphdata.xmax ||
-               Ybot < graphdata.ymin || Ybot > graphdata.ymax) continue;
-            float x[5] = {Xbot, Xbot + scale, Xbot + scale, Xbot, Xbot};
-            float y[5] = {Ybot, Ybot, Ybot + scale, Ybot + scale, Ybot};
-            KapaPrepPlot (kapa, 5, &graphdata);
-            KapaPlotVector (kapa, 5, x, "x");
-            KapaPlotVector (kapa, 5, y, "y");
-        }
-    }
-
-    //ask for user input and finish
-    psastroVisualAskUser (&plotRemoveClumps);
-    psFree (xVec);
-    psFree (yVec);
-
-    return true;
-}
-
-
-/**
- * Assess the goodness of fit for a signle chip by
- * plotting the fit residuals
- * invoked during psastroOneChipFit
- */
-bool psastroVisualPlotOneChipFit (psArray *rawstars, ///< stars detected in the image
-                                  psArray *refstars, ///< reference stars over the same region
-                                  psArray *match,    ///< contains which rawstars match to which refstars
-                                  psMetadata *recipe ///< data reduction recipe
-                                  )
-{
-
-    //make sure we want to plot this
-    if (!isVisual || !plotOneChipFit)
-        return true;
-
-    //plot the residuals
-    if (!psastroVisualResidPlot(rawstars, refstars, match, recipe, "Single Chip Fit Residuals (Chip Coordinates)"))
-        return false;
-
-    //ask for user input and finish
-    psastroVisualAskUser(&plotOneChipFit);
-    return true;
-}
-
-
-/**
- * Plots the chip corners in the FP before and after chips with inconsistent solutions have been fixed.
- * Invoked during psastroFixChips
- */
-bool psastroVisualPlotFixChips (pmFPAfile *input, ///< focal plane array file
-                                psVector *xOld, ///< old X location of chip cornerss
-                                psVector *yOld ///< old Y location of chip corners
-                                )
-{
-    //make sure we want to plot this
-    if(!isVisual || !plotFixChips) return true;
-
-    if(!psastroVisualInitWindow(&kapa, "psastro:plots")) return false;
-
-    KapaSection section = {"s1", .05, .05, .9, .9};
-    Graphdata graphdata;
-    KapaInitGraph( &graphdata);
-    KapaClearPlots (kapa);
-    graphdata.ptype = 2;
-    graphdata.style = 2;
-
-    psVector *xNew = psVectorAllocEmpty (xOld->n, PS_TYPE_F32);
-    psVector *yNew = psVectorAllocEmpty (yOld->n, PS_TYPE_F32);
-
-    // copy of the code in psastroFixChips that generated xOld, yOld, but for xNew, yNew
-    pmFPAview *view = pmFPAviewAlloc (0);
-
-    pmChip *obsChip = NULL;
-    while ((obsChip = pmFPAviewNextChip (view, input->fpa, 1)) != NULL) {
-        if (!obsChip->process || !obsChip->file_exists || !obsChip->data_exists) { continue; }
-
-        psRegion *region = pmChipPixels (obsChip);
-        psPlane ptCP, ptFP;
-
-        ptCP.x = region->x0; ptCP.y = region->y0;
-        psPlaneTransformApply (&ptFP, obsChip->toFPA, &ptCP);
-	psVectorAppend (xNew, ptFP.x);
-	psVectorAppend (yNew, ptFP.y);
-
-        ptCP.x = region->x0; ptCP.y = region->y1;
-        psPlaneTransformApply (&ptFP, obsChip->toFPA, &ptCP);
-	psVectorAppend (xNew, ptFP.x);
-	psVectorAppend (yNew, ptFP.y);
-
-        ptCP.x = region->x1; ptCP.y = region->y1;
-        psPlaneTransformApply (&ptFP, obsChip->toFPA, &ptCP);
-	psVectorAppend (xNew, ptFP.x);
-	psVectorAppend (yNew, ptFP.y);
-
-        ptCP.x = region->x1; ptCP.y = region->y0;
-        psPlaneTransformApply (&ptFP, obsChip->toFPA, &ptCP);
-	psVectorAppend (xNew, ptFP.x);
-	psVectorAppend (yNew, ptFP.y);
-
-        psFree (region);
-    }
-
-    //set up graph
-    psastroVisualScaleGraphdata(&graphdata, xOld, yOld, true);
-    psastroVisualInitGraph(kapa, &section, &graphdata);
-    KapaSendLabel (kapa, "L (FP)", KAPA_LABEL_XM);
-    KapaSendLabel (kapa, "M (FP)", KAPA_LABEL_YM);
-    KapaSendLabel (kapa, "Chip corners before (black) and after (red) FixChips", KAPA_LABEL_XP);
-    KapaPrepPlot (kapa, xOld->n, &graphdata);
-    KapaPlotVector (kapa, xOld->n, xOld->data.F32, "x");
-    KapaPlotVector (kapa, xOld->n, yOld->data.F32, "y");
-
-    graphdata.ptype = 1;
-    graphdata.color = KapaColorByName("red");
-    KapaPrepPlot (kapa, xNew->n, &graphdata);
-    KapaPlotVector (kapa, xNew->n, xNew->data.F32, "x");
-    KapaPlotVector (kapa, xNew->n, yNew->data.F32, "y");
-
-    psastroVisualAskUser(&plotFixChips);
-    psFree(xNew);
-    psFree(yNew);
-    psFree(view);
-    return true;
-}
-
-
-/**
- *  Plots the fpa chip corners projected on to the tangential plane before and after
- *  the astrometry solution has been applied. In psastroAstromGuessCheck, the old corners
- *  are then fit to the new corners to get a sense at how far off the initial WCS info was
- *  in offset, rotation, and scale. This procedure also plots the residuals of the fit from
- *  old to new coordinates
- */
-bool psastroVisualPlotAstromGuessCheck (psVector *cornerPo, ///< P coordinates of chip corners before fitting
-                                        psVector *cornerQo, ///< Q coordinates of chip corners before fitting
-                                        psVector *cornerPn, ///< P coordinates of chip corners after fitting
-                                        psVector *cornerQn, ///< Q coordinates of chip corners after fitting
-                                        psVector *cornerPd, ///< P coordinate residuals of fit from old to new coordinates
-                                        psVector *cornerQd  ///< Q coordinate residuals of fit from old to new coordinates
-                                        )
-{
-
-    //make sure we want to plot this
-    if (!isVisual || !plotAstromGuessCheck) return true;
-
-    //set up graph window
-    if ( !psastroVisualInitWindow (&kapa, "psastro:plots"))
-        return false;
-    Graphdata graphdata;
-    KapaSection section;
-    KapaInitGraph(&graphdata);
-    KapaClearPlots (kapa);
-
-    graphdata.color = KapaColorByName ("black");
-    graphdata.ptype = 2;
-    graphdata.size = 0.5;
-    graphdata.style = 2;
-
-    section.dx = 0.4;
-    section.dy = 0.4;
-
-    //Old Corners
-    section.x = 0.30;
-    section.y = 0.50;
-    section.name = NULL;
-    psStringAppend (&section.name, "a0");
-    KapaSetSection (kapa, &section);
-    psFree(section.name);
-
-    psastroVisualScaleGraphdata (&graphdata, cornerPo, cornerPo, true);
-    KapaSetLimits (kapa, &graphdata);
-    KapaBox (kapa, &graphdata);
-    KapaSendLabel (kapa, "P (Pixels)", KAPA_LABEL_XM);
-    KapaSendLabel (kapa, "Q (Pixels)", KAPA_LABEL_YM);
-    KapaSendLabel (kapa,
-                   "Fiducial Points in the Tangent Plane. Black: Initial Astrometry. Red: Final Astrometry",
-                   KAPA_LABEL_XP);
-    KapaPrepPlot (kapa, cornerPo->n, &graphdata);
-    KapaPlotVector (kapa, cornerPo->n, cornerPo->data.F32, "x");
-    KapaPlotVector (kapa, cornerQo->n, cornerQo->data.F32, "y");
-
-    // New Corners
-    graphdata.color = KapaColorByName("red");
-    graphdata.ptype = 7;
-    graphdata.size = 1.5;
-    KapaPrepPlot (kapa, cornerPn->n, &graphdata);
-    KapaPlotVector (kapa, cornerPn->n, cornerPn->data.F32, "x");
-    KapaPlotVector (kapa, cornerQn->n, cornerQn->data.F32, "y");
-
-    // Residuals
-    psVector *xResid = psVectorAlloc(cornerPn->n, PS_DATA_F32);
-    psVector *yResid = psVectorAlloc(cornerQn->n, PS_DATA_F32);
-    for(int i=0; i < cornerPn->n; i++) {
-        xResid->data.F32[i] = (cornerPd->data.F32[i]);
-        yResid->data.F32[i] = (cornerQd->data.F32[i]);
-    }
-
-    graphdata.color = KapaColorByName("black");
-    graphdata.size=0.5;
-    section.x = 0.3;
-    section.y = 0.0;
-    section.name = NULL;
-    psStringAppend (&section.name, "a1");
-    KapaSetSection (kapa, &section);
-    psFree(section.name);
-
-    psastroVisualScaleGraphdata (&graphdata, xResid, yResid, true);
-    KapaSetLimits (kapa, &graphdata);
-    KapaBox (kapa, &graphdata);
-    KapaSendLabel (kapa, "dP", KAPA_LABEL_XM);
-    KapaSendLabel (kapa, "dQ", KAPA_LABEL_YM);
-    KapaSendLabel (kapa,
-                   "Residual of the Fit from the Initial Astrometry to the Final Astrometry",
-                   KAPA_LABEL_XP);
-    KapaPrepPlot (kapa, cornerPd->n, &graphdata);
-    KapaPlotVector (kapa, cornerPd->n, xResid->data.F32, "x");
-    KapaPlotVector (kapa, cornerQd->n, yResid->data.F32, "y");
-
-    psFree(xResid);
-    psFree(yResid);
-    psastroVisualAskUser (&plotAstromGuessCheck);
-    return true;
-}
-
-
-/**
- * Plots the pixel scales of the fpa before they are
- * equalized in psastroMosaicCommonScale
- */
-bool psastroVisualPlotCommonScale (pmFPA *fpa,         ///< the fpa
-                                   psVector *oldScale  ///< the old pixel scale of each chip in the fpa
-                                   )
-{
-    //make sure we want to plot this
-    if (!isVisual || !plotCommonScale) return false;
-
-    if (!psastroVisualInitWindow(&kapa, "psastro:plots")) return false;
-
-    KapaSection section = {"s1", .05, .05, .9, .9};
-    Graphdata graphdata;
-    psPlane ptCH, ptFP;
-    ptCH.x = 0;
-    ptCH.y = 0;
-    psVector *xVec = psVectorAlloc (oldScale->n, PS_TYPE_F32);
-    psVector *yVec = psVectorAlloc (oldScale->n, PS_TYPE_F32);
-
-    int nobj = 0;
-
-    //project each chip corner to the Focal Plane
-    for(int i = 0; i < fpa->chips->n; i++) {
-        pmChip *chip = fpa->chips->data[i];
-        if (!chip->process || !chip->file_exists) { continue; }
-        if (!chip->toFPA) { continue; }
-
-        psPlaneTransformApply (&ptFP, chip->toFPA, &ptCH);
-        xVec->data.F32[nobj] = ptFP.x;
-        yVec->data.F32[nobj] = ptFP.y;
-        nobj++;
-    }
-
-    //set up plot window
-    KapaInitGraph (&graphdata);
-    KapaClearPlots (kapa);
-    KapaSetSection (kapa, &section);
-    KapaSetFont (kapa, "helvetica", 14);
-    psastroVisualTriplePlot (kapa, &graphdata, xVec, yVec, oldScale, false);
-    KapaSendLabel (kapa, "L (FP)", KAPA_LABEL_XM);
-    KapaSendLabel (kapa, "M (FP)", KAPA_LABEL_YM);
-    KapaSendLabel (kapa, "Old Pixel Scale of FPA Chips (Not to Scale)", KAPA_LABEL_XP);
-
-    psastroVisualAskUser (&plotCommonScale);
-    return true;
-}
-
-
-/**
- *   plot the residuals between raw stars and ref stars after
- *   fitting in psastroMosaicOneChip
- */
-bool psastroVisualPlotMosaicOneChip (psArray *rawstars, psArray *refstars,
-                                     psArray *match, psMetadata *recipe)
-{
-
-    //make sure we want to plot this
-    if (!isVisual || !plotMosaicOneChip) return false;
-
-    //plot the residuals
-    if (!psastroVisualResidPlot(rawstars, refstars, match, recipe, "Single Chip Fit Residuals - Mosaic Mode"))
-        return false;
-
-    //ask for user input and finish
-    psastroVisualAskUser(&plotMosaicOneChip);
-
-    return true;
-}
-
-/** psastroVisualPlotMosaicMatches
- * Plot the matches between raw and reference stars during psastroVisualMosaicSetMatch
- */
-bool psastroVisualPlotMosaicMatches( psArray *rawstars, psArray *refstars,
-                                    psArray *match, int iteration,
-                                    psMetadata *recipe)
-{
-    //make sure we want to plot this
-    if (!isVisual || !plotMosaicMatches) return true;
-
-    char title[60];
-    sprintf(title, "Matches found during psastroMosaicSetMatch iteration %d", iteration);
-
-    if (!psastroVisualResidPlot(rawstars, refstars, match, recipe, title))
-        return false;
-
-    //ask for user input
-    psastroVisualAskUser (&plotMosaicMatches);
-    return true;
-}
-
-
-/*********************/
-/** Helper Routines **/
-/*********************/
-
-
-/** psastroVisualInitGraph (kapa, *section, *graphdata)
- * Initializes graph, sets the section, sets the font,
- * sets the limits, draws the box
- */
-bool psastroVisualInitGraph (int kapa, KapaSection *section, Graphdata *graphdata)
-{
-    KapaSetSection (kapa, section);
-    KapaSetFont (kapa, "helvetica", 14);
-    KapaSetLimits (kapa, graphdata);
-    KapaBox (kapa, graphdata);
-    return true;
-}
-
-
-/** psastroVisualTriplePlot
- * plot 2 vectors whose point sizes are scaled by a third vector
- * autoscales the plot
- */
-bool psastroVisualTriplePlot (int kapid, Graphdata *graphdata, psVector *xVec, psVector *yVec, psVector *zVec, bool increasing)
-{
-    psastroVisualScaleGraphdata (graphdata, xVec, yVec, true);
-    //printf("%f %f %f %f \n",graphdata->xmin, graphdata->xmax, graphdata->ymin, graphdata->ymax);
-    // set the scale vector
-    psVector *zScale = psVectorAlloc (zVec->n, PS_DATA_F32);
-    psastroVisualCreateScaleVec (zVec, zScale, increasing);
-
-    KapaSetFont (kapid, "helvetica", 14);
-    KapaSetLimits(kapid, graphdata);
-    KapaBox (kapid, graphdata);
-
-    // the point size will be scaled from the z vector
-    graphdata->ptype = 7;
-    graphdata->style = 2;
-    graphdata->size = -1;
-    KapaPrepPlot (kapid, xVec->n, graphdata);
-    KapaPlotVector (kapid, xVec->n, xVec->data.F32, "x");
-    KapaPlotVector (kapid, yVec->n, yVec->data.F32, "y");
-    KapaPlotVector (kapid, zVec->n, zScale->data.F32, "z");
-    psFree (zScale);
-    return true;
-}
-
-
-/** psastroVisualTripleOverplot
- * same as above, but does not rescale the plot or redraw the bounding box
- */
-bool psastroVisualTripleOverplot (int kapid, Graphdata *graphdata, psVector *xVec, psVector *yVec, psVector *zVec, bool increasing) {
-
-    // set the scale vector
-    psVector *zScale = psVectorAlloc (zVec->n, PS_DATA_F32);
-    psastroVisualCreateScaleVec (zVec, zScale, increasing);
-
-    KapaSetFont (kapid, "helvetica", 14);
-
-    // the point size will be scaled from the z vector
-    graphdata->size = -1;
-    KapaPrepPlot (kapid, xVec->n, graphdata);
-    KapaPlotVector (kapid, xVec->n, xVec->data.F32, "x");
-    KapaPlotVector (kapid, yVec->n, yVec->data.F32, "y");
-    KapaPlotVector (kapid, zVec->n, zScale->data.F32, "z");
-    psFree (zScale);
-    return true;
-}
-
-
-/** psastroVisualCreateScaleVec
- * create a vector of plot point sizes from a vector
- */
-bool psastroVisualCreateScaleVec (psVector *zVec, psVector *zScale, bool increasing) {
-    // set limits based on data values
-    float zmin = +FLT_MAX;
-    float zmax = -FLT_MAX;
-
-    for (int i = 0; i < zVec->n; i++) {
-        zmin = PS_MIN (zmin, zVec->data.F32[i]);
-        zmax = PS_MAX (zmax, zVec->data.F32[i]);
-    }
-
-    float range = zmax - zmin;
-    if (range == 0.0) {
-        psVectorInit (zScale, 1.0);
-    } else {
-        for (int i = 0; i < zVec->n; i++) {
-            if (increasing) {
-                zScale->data.F32[i] = PS_MIN (1.5, PS_MAX(0.05, 1.5*(zVec->data.F32[i] - zmin)/range));
-            } else {
-                zScale->data.F32[i] = PS_MIN (1.5, PS_MAX(0.05, 1.5*(zmax - zVec->data.F32[i])/range));
-            }
-        }
-    }
-    return true;
-}
-
-
-/** psastroVisualScaleGraphdata
- * Scale the graphdata structure based on x and y coordinates. Use sigma clipping to
- * prevent outliers from making te plot region too big.
- */
-bool psastroVisualScaleGraphdata(Graphdata *graphdata, psVector *xVec, psVector *yVec, bool clip) {
-
-    graphdata->xmin = +FLT_MAX;
-    graphdata->xmax = -FLT_MAX;
-    graphdata->ymin = +FLT_MAX;
-    graphdata->ymax = -FLT_MAX;
-
-    //determine standard deviation of xVec and yVec
-    psStats *statsX = psStatsAlloc(PS_STAT_SAMPLE_MEDIAN | PS_STAT_SAMPLE_STDEV);
-    psStats *statsY = psStatsAlloc(PS_STAT_SAMPLE_MEDIAN | PS_STAT_SAMPLE_STDEV);
-    psVectorStats (statsX, xVec, NULL, NULL, 0);
-    psVectorStats (statsY, yVec, NULL, NULL, 0);
-
-    float xhi = +FLT_MAX, xlo = -FLT_MAX, yhi = +FLT_MAX, ylo = -FLT_MAX;
-    if (clip) {
-        xhi  = statsX->sampleMedian + 3 *statsX->sampleStdev;
-        xlo = statsX->sampleMedian - 3 *statsX->sampleStdev;
-        yhi = statsY->sampleMedian + 3 *statsY->sampleStdev;
-        ylo = statsY->sampleMedian - 3 *statsY->sampleStdev;
-    }
-
-    // abort if there is no good data
-    if (!isfinite(xhi) || !isfinite(xlo) || !isfinite(yhi) || !isfinite(ylo)) {
-        graphdata->xmin = -1;
-        graphdata->ymin  = -1;
-        graphdata->xmax = 1;
-        graphdata->ymax = 1;
-        psFree(statsX);
-        psFree(statsY);
-        return false;
-    }
-
-    for(int i = 0; i < xVec->n; i++) {
-        if (!isfinite(xVec->data.F32[i])) continue;
-        if (xVec->data.F32[i] > xhi || xVec->data.F32[i] < xlo) continue;
-        graphdata->xmin = PS_MIN (graphdata->xmin, xVec->data.F32[i]);
-        graphdata->xmax = PS_MAX (graphdata->xmax, xVec->data.F32[i]);
-    }
-
-    for (int i = 0; i < yVec->n; i++) {
-        if (!isfinite(xVec->data.F32[i])) continue;
-        if (yVec->data.F32[i] > yhi || yVec->data.F32[i] < ylo) continue;
-        graphdata->ymin = PS_MIN (graphdata->ymin, yVec->data.F32[i]);
-        graphdata->ymax = PS_MAX (graphdata->ymax, yVec->data.F32[i]);
-    }
-
-    // add a whitespace border
-    float range = graphdata->xmax - graphdata->xmin;
-    if (range == 0) range = 1;
-    graphdata->xmin -= .05 * range;
-    graphdata->xmax += .05 * range;
-
-    range = graphdata->ymax - graphdata->ymin;
-    if (range == 0) range = 1;
-    graphdata->ymin -= .05 * range;
-    graphdata->ymax += .05 * range;
-
-    psFree (statsX);
-    psFree (statsY);
-    return true;
-}
-
-
-/** psastroVisualResdiPlot
- * Plots the residuals between matched raw and reference stars. Used to assess the quality of an
- * astrometry solution
- */
-bool psastroVisualResidPlot (psArray *rawstars, psArray *refstars, psArray *match, psMetadata *recipe, char *title) {
-
-    //set up the first window
-    if (!psastroVisualInitWindow( &kapa, "psastro:plots")) return false;
-
-    //initialize graph information
-    Graphdata graphdata;
-    KapaSection section;
-
-    KapaInitGraph (&graphdata);
-    KapaClearPlots (kapa);
-
-    graphdata.color = KapaColorByName ("black");
-    graphdata.ptype = 7;
-    graphdata.size = 0.5;
-    graphdata.style = 2;
-
-    section.dx = 0.4;
-    section.dy = 0.4;
-
-    //initialize and populate the plotting vectors
-    bool status = false;
-    float iMagMin = psMetadataLookupF32 (&status, recipe, "PSASTRO.PLOT.INST.MAG.MIN");
-    float iMagMax = psMetadataLookupF32 (&status, recipe, "PSASTRO.PLOT.INST.MAG.MAX");
-    float rMagMin = psMetadataLookupF32 (&status, recipe, "PSASTRO.PLOT.REF.MAG.MIN");
-    float rMagMax = psMetadataLookupF32 (&status, recipe, "PSASTRO.PLOT.REF.MAG.MAX");
-
-    psVector *xVec = psVectorAlloc (match->n, PS_TYPE_F32);
-    psVector *yVec = psVectorAlloc (match->n, PS_TYPE_F32);
-    psVector *zVec = psVectorAlloc (match->n, PS_TYPE_F32);
-
-    // X vs dX
-    section.x = 0.0;
-    section.y = 0.5;
-    section.name = NULL;
-    psStringAppend (&section.name, "a0");
-    KapaSetSection (kapa, &section);
-    psFree (section.name);
-
-    int n = 0;
-    for (int i = 0; i < match->n; i++) {
-        pmAstromMatch *pair = match->data[i];
-        pmAstromObj *raw = rawstars->data[pair->raw];
-        pmAstromObj *ref = refstars->data[pair->ref];
-
-        if (!isfinite(raw->Mag)) continue;
-        if (raw->Mag < iMagMin) continue;
-        if (raw->Mag > iMagMax) continue;
-        if (ref->Mag < rMagMin) continue;
-        if (ref->Mag > rMagMax) continue;
-
-        xVec->data.F32[n] = raw->chip->x;
-        yVec->data.F32[n] = raw->chip->x - ref->chip->x;
-        zVec->data.F32[n] = raw->Mag;
-        n++;
-    }
-    xVec->n = yVec->n = zVec->n = n;
-
-    KapaSendLabel (kapa, "X", KAPA_LABEL_XM);
-    KapaSendLabel (kapa, "dX", KAPA_LABEL_YM);
-    psastroVisualTriplePlot (kapa, &graphdata, xVec, yVec, zVec, false);
-
-    // X vs dY
-    section.x = 0.5;
-    section.y = 0.5;
-    section.name = NULL;
-    psStringAppend (&section.name, "a1");
-    KapaSetSection (kapa, &section);
-    psFree (section.name);
-
-    n = 0;
-    for (int i = 0; i < match->n; i++) {
-        pmAstromMatch *pair = match->data[i];
-        pmAstromObj *raw = rawstars->data[pair->raw];
-        pmAstromObj *ref = refstars->data[pair->ref];
-
-        if (!isfinite(raw->Mag)) continue;
-        if (raw->Mag < iMagMin) continue;
-        if (raw->Mag > iMagMax) continue;
-        if (ref->Mag < rMagMin) continue;
-        if (ref->Mag > rMagMax) continue;
-
-        xVec->data.F32[n] = raw->chip->x;
-        yVec->data.F32[n] = raw->chip->y - ref->chip->y;
-        zVec->data.F32[n] = raw->Mag;
-        n++;
-    }
-    xVec->n = yVec->n = zVec->n = n;
-
-    KapaSendLabel (kapa, "X", KAPA_LABEL_XM);
-    KapaSendLabel (kapa, "dY", KAPA_LABEL_YM);
-    psastroVisualTriplePlot (kapa, &graphdata, xVec, yVec, zVec, false);
-
-    // Y vs dX
-    section.x = 0.0;
-    section.y = 0.0;
-    section.name = NULL;
-    psStringAppend (&section.name, "a2");
-    KapaSetSection (kapa, &section);
-    psFree (section.name);
-
-    n = 0;
-    for (int i = 0; i < match->n; i++) {
-        pmAstromMatch *pair = match->data[i];
-        pmAstromObj *raw = rawstars->data[pair->raw];
-        pmAstromObj *ref = refstars->data[pair->ref];
-
-        if (!isfinite(raw->Mag)) continue;
-        if (raw->Mag < iMagMin) continue;
-        if (raw->Mag > iMagMax) continue;
-        if (ref->Mag < rMagMin) continue;
-        if (ref->Mag > rMagMax) continue;
-
-        xVec->data.F32[n] = raw->chip->y;
-        yVec->data.F32[n] = raw->chip->x - ref->chip->x;
-        zVec->data.F32[n] = raw->Mag;
-        n++;
-    }
-    xVec->n = yVec->n = zVec->n = n;
-
-    KapaSendLabel (kapa, "Y", KAPA_LABEL_XM);
-    KapaSendLabel (kapa, "dX", KAPA_LABEL_YM);
-    psastroVisualTriplePlot (kapa, &graphdata, xVec, yVec, zVec, false);
-
-    // Y vs dY
-    section.x = 0.5;
-    section.y = 0.0;
-    section.name = NULL;
-    psStringAppend (&section.name, "a3");
-    KapaSetSection (kapa, &section);
-    psFree (section.name);
-
-    n = 0;
-    for (int i = 0; i < match->n; i++) {
-        pmAstromMatch *pair = match->data[i];
-        pmAstromObj *raw = rawstars->data[pair->raw];
-        pmAstromObj *ref = refstars->data[pair->ref];
-
-        if (!isfinite(raw->Mag)) continue;
-        if (raw->Mag < iMagMin) continue;
-        if (raw->Mag > iMagMax) continue;
-        if (ref->Mag < rMagMin) continue;
-        if (ref->Mag > rMagMax) continue;
-
-        xVec->data.F32[n] = raw->chip->y;
-        yVec->data.F32[n] = raw->chip->y - ref->chip->y;
-        zVec->data.F32[n] = raw->Mag;
-        n++;
-    }
-    xVec->n = yVec->n = zVec->n = n;
-
-    KapaSendLabel (kapa, "Y", KAPA_LABEL_XM);
-    KapaSendLabel (kapa, "dY", KAPA_LABEL_YM);
-    pmKapaPlotVectorTriple_AutoLimits_OpenGraph (kapa, &graphdata, xVec, yVec, zVec, false);
-
-    section.x = 0.0;
-    section.y = 0.0;
-    section.dx = 0.95;
-    section.dy = 0.95;
-    section.name = NULL;
-    psStringAppend (&section.name, "a5");
-    KapaSetSection (kapa, &section);
-    KapaSendLabel (kapa, title, KAPA_LABEL_XP);
-    psFree (section.name);
-
-
-    // X vs Y plot (different window)
-    if (!psastroVisualInitWindow( &kapa2, "psastro:plots"))
-        return false;
-
-    KapaInitGraph (&graphdata);
-    KapaClearPlots (kapa2);
-
-    graphdata.color = KapaColorByName ("black");
-    graphdata.ptype = 2;
-    graphdata.style = 2;
-
-    psFree (xVec);
-    psFree (yVec);
-    psFree (zVec);
-
-    xVec = psVectorAlloc (rawstars->n, PS_TYPE_F32);
-    yVec = psVectorAlloc (rawstars->n, PS_TYPE_F32);
-    zVec = psVectorAlloc (rawstars->n, PS_TYPE_F32);
-
-    // X vs Y by mag (raw)
-    n = 0;
-    for (int i = 0; i < rawstars->n; i++) {
-        pmAstromObj *raw = rawstars->data[i];
-        if (!isfinite(raw->Mag)) continue;
-        if (raw->Mag < iMagMin) continue;
-        if (raw->Mag > iMagMax) continue;
-
-        xVec->data.F32[n] = raw->chip->x;
-        yVec->data.F32[n] = raw->chip->y;
-        zVec->data.F32[n] = raw->Mag;
-        n++;
-    }
-    xVec->n = yVec->n = zVec->n = n;
-
-    KapaSendLabel (kapa2, "X", KAPA_LABEL_XM);
-    KapaSendLabel (kapa2, "Y", KAPA_LABEL_YM);
-    KapaSendLabel (kapa2,
-                   "Chip Coordinates. Black = Raw Stars. Red = Ref Stars. Blue = Matched Stars"
-                   , KAPA_LABEL_XP);
-    psastroVisualTriplePlot (kapa2, &graphdata, xVec, yVec, zVec, false);
-
-    // X vs Y by mag (ref)
-    psFree (xVec);
-    psFree (yVec);
-    psFree (zVec);
-
-    xVec = psVectorAlloc (refstars->n, PS_TYPE_F32);
-    yVec = psVectorAlloc (refstars->n, PS_TYPE_F32);
-    zVec = psVectorAlloc (refstars->n, PS_TYPE_F32);
-
-    graphdata.color = KapaColorByName ("red");
-    graphdata.ptype = 7;
-    graphdata.style = 2;
-
-    n = 0;
-    for (int i = 0; i < refstars->n; i++) {
-        pmAstromObj *ref = refstars->data[i];
-        if (!isfinite(ref->Mag)) continue;
-        if (ref->Mag < rMagMin) continue;
-        if (ref->Mag > rMagMax) continue;
-
-        xVec->data.F32[n] = ref->chip->x;
-        yVec->data.F32[n] = ref->chip->y;
-        zVec->data.F32[n] = ref->Mag;
-        n++;
-    }
-    xVec->n = yVec->n = zVec->n = n;
-    psastroVisualTripleOverplot (kapa2, &graphdata, xVec, yVec, zVec, false);
-
-    //rescale the graph to include all points
-    float xmin = graphdata.xmin;
-    float ymin = graphdata.ymin;
-    float xmax = graphdata.xmax;
-    float ymax = graphdata.ymax;
-    psastroVisualScaleGraphdata(&graphdata, xVec, yVec, true);
-    graphdata.xmin = PS_MIN(xmin, graphdata.xmin);
-    graphdata.ymin = PS_MIN(ymin, graphdata.ymin);
-    graphdata.xmax = PS_MAX(xmax, graphdata.xmax);
-    graphdata.ymax = PS_MAX(ymax, graphdata.ymax);
-    KapaSetLimits (kapa2, &graphdata);
-
-    //overplot matched stars in blue
-    psFree (xVec);
-    psFree (yVec);
-    psFree (zVec);
-
-    xVec = psVectorAlloc (match->n, PS_TYPE_F32);
-    yVec = psVectorAlloc (match->n, PS_TYPE_F32);
-    zVec = psVectorAlloc (match->n, PS_TYPE_F32);
-
-    graphdata.color = KapaColorByName ("blue");
-    n = 0;
-    for (int i = 0; i < match->n; i++) {
-        pmAstromMatch *pair = match->data[i];
-        pmAstromObj *raw = rawstars->data[pair->raw];
-        pmAstromObj *ref = refstars->data[pair->ref];
-        if (raw->Mag < iMagMin) continue;
-        if (raw->Mag > iMagMax) continue;
-        if (ref->Mag < rMagMin) continue;
-        if (ref->Mag > rMagMax) continue;
-
-        xVec->data.F32[n] = raw->chip->x;
-        yVec->data.F32[n] = raw->chip->y;
-        zVec->data.F32[n] = iMagMin;
-        n++;
-    }
-    xVec->n = yVec->n = zVec->n = n;
-    psastroVisualTripleOverplot (kapa2, &graphdata, xVec, yVec, zVec, false);
-
-    psFree (xVec);
-    psFree (yVec);
-    psFree (zVec);
-    return true;
-}
-
-// END OF PROGRAM
-#else
-
-bool psastroSetVisual (bool mode) {return true};
-bool psastroVisualClose() {return true};
-bool psastroVisualPlotLuminosityFunction (psVector *lnMag, psVector *Mag, pmLumFunc *lumFunc, pmLumFunc *rawFunc) {return true};
-bool psastroVisualPlotRawStars (psArray *rawstars, pmFPA *fpa, pmChip *chip, psMetadata *recipe) {return true};
-bool psastroVisualPlotRefStars (psArray *refstars, psMetadata *recipe) {return true};
-bool psastroVisualPlotRemoveClumps (psArray *input, psImage *count, int scale, float limit) {return true};
-bool psastroVisualPlotFixChips (pmFPAfile *input, psVector *xOld, psVector *yOld) {return true};
-bool psastroVisualPlotOneChipFit (psArray *rawstars, psArray *refstars, psArray *match, psMetadata *recipe) {return true};
-bool psastroVisualPlotAstromGuessCheck (psVector *cornerPo, psVector *cornerQo, psVector *cornerPn, psVector *cornerQn, psVector *cornerPd, psVector *cornerQd) {return true};
-bool psastroVisualPlotMosaicOneChip (psArray *rawstars, psArray *refstars, psArray *match, psMetadata *recipe) {return true};
-bool psastroVisualPlotCommonScale (pmFPA *fpa, psVector *oldScale) {return true};
-bool psastroVisualPlotMosaicMatches (psArray *rawstars, psArray *refstars, psArray *match, int iteration, psMetadata *recipe) {return true};
-
-# endif
-
Index: /branches/cnb_branches/cnb_branch_20090301/psconfig/psbuild
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psconfig/psbuild	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/psconfig/psbuild	(revision 23352)
@@ -13,4 +13,5 @@
 $stop = "";
 $verbose = 0;
+$use_svn = 1;
 
 $extlibs = "none";
@@ -83,4 +84,8 @@
     if ($ARGV[0] eq "-bootstrap") {
         &bootstrap ();
+    }
+    if ($ARGV[0] eq "-skip-svn") {
+	$use_svn = 0;
+	shift; next;
     }
     if ($ARGV[0] eq "-env") {
@@ -157,6 +162,61 @@
 sub build_distribution {
 
+    # set environment variables used to supply SVN info to the compilation
+
+    if ($use_svn) {
+	# example dump from svn info:
+	# pikake: svn info
+	# Path: .
+	# URL: https://svn.pan-starrs.ifa.hawaii.edu/repo/ipp/branches/eam_branches/eam_branch_20090303/ppImage
+	# Repository Root: https://svn.pan-starrs.ifa.hawaii.edu/repo/ipp
+	# Repository UUID: 60eb6cdc-a59c-4636-a4e0-dba66a9721fd
+	# Revision: 23158
+	# Node Kind: directory
+	# Schedule: normal
+	# Last Changed Author: price
+	# Last Changed Rev: 23125
+	# Last Changed Date: 2009-03-03 15:41:16 -1000 (Tue, 03 Mar 2009)
+	
+	$svn_version = `svnversion`; chomp $svn_version;
+	@svn_info = `svn info`;
+
+	# get the svn_root first:
+	foreach $line (@svn_info) {
+	    if ($line =~ m|^Repository Root:|) {
+		($svn_root) = $line =~ m|^Repository Root:\s*(\S*)|;
+		last;
+	    }
+	}
+
+	# now get the branch and UUID values
+	foreach $line (@svn_info) {
+	    if ($line =~ m|^URL:|) {
+		($svn_branch) = $line =~ m|^URL:\s*$svn_root/*(\S*)|;
+	    }
+	    if ($line =~ m|^Repository UUID:|) {
+		($svn_source) = $line =~ m|^Repository UUID:\s*(\S*)|;
+	    }
+	}
+	
+	$ENV{SVN_VERSION} = $svn_version;
+	$ENV{SVN_BRANCH}  = $svn_branch;
+	$ENV{SVN_SOURCE}  = $svn_source;
+    } else {
+	# alternatively, grab these from the following files:
+	if (! -e "SVNINFO") { 
+	    print "missing SVNINFO file for repository info, skipping\n";
+	} else {
+	    @svn_info = `cat SVNINFO`;
+	    foreach $line (@svn_info) {
+		($name, $value) = split (" ", $line);
+		$ENV{$name} = $value;
+	    }
+	}
+    }
+    print "SVN_VERSION $ENV{SVN_VERSION}\n";
+    print "SVN_BRANCH  $ENV{SVN_BRANCH}\n";
+    print "SVN_SOURCE  $ENV{SVN_SOURCE}\n";
+
     # use psconfig.csh to set needed build aliases
-
     if ($extlibs eq "check") {
         $status = vsystem ("pschecklibs");
@@ -247,12 +307,12 @@
 
         if (-e "Build.PL") {
-            vsystem ("$psperlbuild");
-            if ($?) { &failure($module[$i], "failure in perl Build.PL"); }
-
-            vsystem ("./Build");
-            if ($?) { &failure($module[$i], "failure in Build"); }
-
-            vsystem ("./Build install");
-            if ($?) { &failure($module[$i], "failure in Build install"); }
+            $status = vsystem ("$psperlbuild");
+            if ($status) { &failure($module[$i], "failure in perl Build.PL"); }
+
+            $status = vsystem ("./Build");
+            if ($status) { &failure($module[$i], "failure in Build"); }
+
+            $status = vsystem ("./Build install");
+            if ($status) { &failure($module[$i], "failure in Build install"); }
 
             next;
@@ -272,11 +332,11 @@
         if ($developer && $rebuild_this && ! -e "configure" && -e "autogen.sh") {
             $skip_configure = 1;
-            vsystem ("$psautogen $psopts");
-            if ($?) { &failure($module[$i], "failure in psautogen"); }
+            $status = vsystem ("$psautogen $psopts");
+            if ($status) { &failure($module[$i], "failure in psautogen"); }
         }
 
         if ($rebuild_this && -e "configure" && !$skip_configure) {
-            vsystem ("$psconfigure $psopts");
-            if ($?) { &failure($module[$i], "failure in psconfigure"); }
+            $status = vsystem ("$psconfigure $psopts");
+            if ($status) { &failure($module[$i], "failure in psconfigure"); }
         }
 
@@ -288,13 +348,13 @@
 
         if ($clean) {
-            vsystem ("$make clean");
-            if ($?) { &failure($module[$i], "failure in make clean"); }
+            $status = vsystem ("$make clean");
+            if ($status) { &failure($module[$i], "failure in make clean"); }
         }
 
-        vsystem ("$make");
-        if ($?) { &failure($module[$i], "failure in make"); }
-
-        vsystem ("$make install");
-        if ($?) { &failure($module[$i], "failure in make install"); }
+        $status = vsystem ("$make");
+        if ($status) { &failure($module[$i], "failure in make"); }
+
+        $status = vsystem ("$make install");
+        if ($status) { &failure($module[$i], "failure in make install"); }
 
         print "*** done with $module[$i] ***\n";
Index: /branches/cnb_branches/cnb_branch_20090301/psconfig/pschecklibs
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psconfig/pschecklibs	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/psconfig/pschecklibs	(revision 23352)
@@ -487,5 +487,5 @@
 
 sub usage {
-    print STDERR "USAGE: pscheckperl [-version] [-build]\n";
+    print STDERR "USAGE: pschecklibs [-version] [-build]\n";
     exit 2;
 }
Index: /branches/cnb_branches/cnb_branch_20090301/psconfig/tagsets/ipp-2.8.dist
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psconfig/tagsets/ipp-2.8.dist	(revision 23352)
+++ /branches/cnb_branches/cnb_branch_20090301/psconfig/tagsets/ipp-2.8.dist	(revision 23352)
@@ -0,0 +1,70 @@
+# necessary fields:                    
+# |-- tag?
+# ||-- build?
+# |||-- package? 
+# ||||-- update directory from CVS?
+# |||||-- build for developer?
+# |||||
+# |||||  module                 branch name      tag version   
+# ||||| 
+  YYNNY  Ohana                  ipp-2-8          -0
+  NNYYN  ohana.base             ipp-2-8          -0
+  NNYYN  libohana               ipp-2-8          -0
+  NNYYN  libfits                ipp-2-8          -0
+  NNYYN  libautocode            ipp-2-8          -0
+  NNYYN  libdvo                 ipp-2-8          -0
+  NNYYN  libkapa                ipp-2-8          -0
+  NNYYN  libtap.ohana           ipp-2-8          -0
+  NNYYN  addstar                ipp-2-8          -0
+  NNYYN  delstar                ipp-2-8          -0
+  NNYYN  getstar                ipp-2-8          -0
+  NNYYN  ohana.tools            ipp-2-8          -0
+  NNYYN  kapa2                  ipp-2-8          -0
+  NNYYN  relphot                ipp-2-8          -0
+  NNYYN  relastro               ipp-2-8          -0
+  NNYYN  uniphot                ipp-2-8          -0
+  NNYYN  opihi.base             ipp-2-8          -0
+  NNYYN  mana                   ipp-2-8          -0
+  NNYYN  dvo                    ipp-2-8          -0
+  NNYYN  pantasks               ipp-2-8          -0
+  NNYYN  pcontrol               ipp-2-8          -0
+  NNYYN  pclient                ipp-2-8          -0      
+          
+  YNNYY  Nebulous/nebclient     ipp-2-8          -0
+  YNNYY  Nebulous               ipp-2-8          -0
+  YYYYY  PS-IPP-Metadata-Config ipp-2-8          -0
+  YYYYY  PS-IPP-Config          ipp-2-8          -0     
+          
+  YYYYY  psLib                  ipp-2-8          -0
+  YYYYY  psModules              ipp-2-8          -0
+  YYYYY  ppStats                ipp-2-8          -0
+  YYYYY  psphot                 ipp-2-8          -0
+  YYYYY  psastro                ipp-2-8          -0
+  YYYYY  ppConfigDump           ipp-2-8          -0
+  YYYYY  ppImage                ipp-2-8          -0
+  YYYYY  ppNorm                 ipp-2-8          -0
+  YYYYY  ppMerge                ipp-2-8          -0
+  YNNYN  pedestal               ipp-2-8          -0
+  YYYYY  dvoTools               ipp-2-8          -0
+  YYYYY  pswarp                 ipp-2-8          -0
+  YYYYY  ppArith                ipp-2-8          -0
+  YYYYY  ppStack                ipp-2-8          -0
+  YYYYY  ppSub                  ipp-2-8          -0
+  YYYYY  ppSim                  ipp-2-8          -0
+          
+  YNNYY  glueforge              ipp-2-8          -0
+  YNNYY  dbconfig               ipp-2-8          -0
+  NNNNY  ippdb.src             
+  YYYNN  ippdb                  ipp-2-8          -0
+  YYYYY  PS-IPP-PStamp          ipp-2-8          -0
+  YYYYY  pstamp                 ipp-2-8          -0
+  YYYYY  ippTools               ipp-2-8          -0
+  YYYYY  ippScripts             ipp-2-8          -0
+  YYYYY  ippTasks               ipp-2-8          -0
+          
+  YYYYY  ippconfig              ipp-2-8          -0
+  YNYYN  psconfig               ipp-2-8          -0
+  YNYYN  ippMonitor             ipp-2-8          -0
+  YYYYY  DataStore              ipp-2-8          -0
+
+# there are externally required C libraries and perl modules (see INSTALL)
Index: /branches/cnb_branches/cnb_branch_20090301/psconfig/tagsets/ipp-2.8.libs
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psconfig/tagsets/ipp-2.8.libs	(revision 23352)
+++ /branches/cnb_branches/cnb_branch_20090301/psconfig/tagsets/ipp-2.8.libs	(revision 23352)
@@ -0,0 +1,94 @@
+# this file defines C libraries and C headers needed by the ipp, and information on finding them
+
+# directories to search: /lib, /usr/lib, /usr/X11R6/lib, etc.
+# modifications based on the architecture (eg, PATH/lib64, etc)
+# additional locations based on the env variables
+# dlltype to use
+
+# each entry contains:
+#   type : lib / include
+#   name 
+#   alternate names
+#   alternate paths
+#   tarball name
+#   tar directory
+#   force install by default?
+#   configure options
+#   make options
+#   make install options
+
+lib libm                 NONE           NONE   NONE                     NONE             N NONE NONE NONE
+lib libX11               NONE           NONE   NONE                     NONE             N NONE NONE NONE
+lib libpthread           NONE           NONE   NONE                     NONE             N NONE NONE NONE
+lib libncurses           curses,termcap NONE   ncurses-5.6.tar.gz       ncurses-5.6      N NONE NONE NONE
+lib libreadline          NONE           NONE   readline-5.2-p12.tar.gz  readline-5.2-p12 Y NONE NONE NONE
+lib libz                 NONE           NONE   zlib-1.2.3.tar.gz        zlib-1.2.3       N --shared NONE NONE
+lib libpng               NONE           NONE   libpng-1.2.15.tar.gz     libpng-1.2.15    N NONE NONE NONE
+lib libjpeg              NONE           jpeg   jpegsrc.v6b-p1.tar.gz    jpeg-6b          N --enable-shared NONE install-lib
+lib libcfitsio           NONE           NONE   cfitsio3100-pap.tar.gz   cfitsio3100-pap  N --enable-shared shared NONE
+#lib libmysqlclient      NONE           mysql  mysql-5.0.27.tar.gz      mysql-5.0.27     N NONE NONE NONE
+lib libmysqlclient       NONE           mysql  mysql-5.0.51a.tar.gz     mysql-5.0.51a    N NONE NONE NONE
+lib libgsl               NONE           NONE   gsl-1.11.tar.gz          gsl-1.11         N NONE NONE NONE
+lib libfftw3f            NONE           NONE   fftw-3.0.1.tar.gz        fftw-3.0.1       N --enable-float,--enable-shared,--disable-fortran NONE NONE
+#lib libfftw3            NONE           NONE   fftw-3.0.1.tar.gz        fftw-3.0.1       N --enable-shared,--disable-fortran NONE NONE
+# paul claims we are not currently using double-point FFTs anywhere
+
+bin pkg-config           NONE           NONE   pkg-config-0.22.tar.gz   pkg-config-0.22  N NONE NONE NONE
+
+inc X11/Xatom.h          NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc X11/Xlib.h           NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc X11/Xresource.h      NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc X11/Xutil.h          NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc X11/cursorfont.h     NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc X11/keysym.h         NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc X11/keysymdef.h      NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc arpa/inet.h          NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc assert.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc complex.h            NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc ctype.h              NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc errno.h              NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc fcntl.h              NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc fftw3.h              NONE           NONE   fftw-3.0.1.tar.gz        fftw-3.0.1       N --enable-float,--enable-shared,--disable-fortran NONE NONE
+inc fitsio.h             NONE           NONE   cfitsio3100-pap.tar.gz   cfitsio3100-pap  N --enable-shared shared NONE
+inc glob.h               NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc gsl/gsl_randist.h    NONE           NONE   gsl-1.11.tar.gz          gsl-1.11         N NONE NONE NONE 
+inc gsl/gsl_rng.h        NONE           NONE   gsl-1.11.tar.gz          gsl-1.11         N NONE NONE NONE 
+inc inttypes.h           NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc jpeglib.h            NONE           jpeg   jpegsrc.v6b.tar.gz       jpeg-6b          N --enable-shared NONE install-lib
+inc limits.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc malloc.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc math.h               NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc memory.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc mysql.h              NONE           mysql  mysql-5.0.51a.tar.gz     mysql-5.0.51a    N NONE NONE NONE
+inc netdb.h              NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc netinet/ip.h         NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc png.h                NONE           NONE   libpng-1.2.15.tar.gz     libpng-1.2.15    N NONE NONE NONE
+inc pthread.h            NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc readline/history.h   NONE           NONE   readline-5.2-p12.tar.gz  readline-5.2-p12 N NONE NONE NONE
+inc readline/readline.h  NONE           NONE   readline-5.2-p12.tar.gz  readline-5.2-p12 N NONE NONE NONE
+inc regex.h              NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc signal.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc stdint.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc stdio.h              NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc stdlib.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc string.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/ipc.h            NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/resource.h       NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/sem.h            NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/socket.h         NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/stat.h           NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/time.h           NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/types.h          NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/uio.h            NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/un.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc sys/wait.h           NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc time.h               NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc unistd.h             NONE           NONE   NONE                     NONE             N NONE NONE NONE 
+inc zlib.h               NONE           NONE   zlib-1.2.3.tar.gz        zlib-1.2.3       N --shared NONE NONE
+
+# xml is currently not used by IPP
+# lib xml2       NONE           NONE   NONE                     NONE             Y NONE NONE NONE
+
+# doxygen is having some unknown build issues on alala
+# bin doxygen            NONE NONE doxygen-1.5.1.src.tar.gz doxygen-1.5.1    N NONE NONE NONE
+
Index: /branches/cnb_branches/cnb_branch_20090301/psconfig/tagsets/ipp-2.8.perl
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psconfig/tagsets/ipp-2.8.perl	(revision 23352)
+++ /branches/cnb_branches/cnb_branch_20090301/psconfig/tagsets/ipp-2.8.perl	(revision 23352)
@@ -0,0 +1,83 @@
+# NN    Name                           Tarball                                  Version        Optional Responses
+  00    Getopt::Long                   Getopt-Long-2.36.tar.gz                  2.3            n
+  00    Module::Build                  Module-Build-0.2806.tar.gz               0.2806
+  01    ExtUtils::MakeMaker            ExtUtils-MakeMaker-6.31.tar.gz           0
+  02    Params::Validate               Params-Validate-0.91.tar.gz       0.77
+#  02    Apache::Test                   Apache-Test-1.29.tar.gz                  1.29
+  03    DateTime::TimeZone             DateTime-TimeZone-0.59.tar.gz            0
+  04    DateTime::Locale               DateTime-Locale-0.33.tar.gz              0
+  05    Time::Local                    Time-Local-1.17.tar.gz                   0
+  06    DateTime                       DateTime-0.36.tar.gz                     0
+  07    MIME::Base64                   MIME-Base64-3.07.tar.gz                  0
+  08    IO::Compress::Base             IO-Compress-Base-2.003.tar.gz            0
+  09    Compress::Raw::Zlib            Compress-Raw-Zlib-2.003.tar.gz           0
+  10    Class::Factory::Util           Class-Factory-Util-1.6.tar.gz            0
+  11    DateTime::Format::Strptime     DateTime-Format-Strptime-1.0700.tar.gz   0
+  12    Net::Domain::TLD               Net-Domain-TLD-1.65.tar.gz               0
+  13    Sub::Uplevel                   Sub-Uplevel-0.14.tar.gz                  0
+  14    HTML::Tagset                   HTML-Tagset-3.10.tar.gz                  0
+  15    Digest                         Digest-1.15.tar.gz                       0
+  16    IO::Compress::Zlib::Extra      IO-Compress-Zlib-2.003.tar.gz            0
+  17    version                        version-0.70.tar.gz                      0
+  18    Text::Balanced                 Text-Balanced-v2.0.0.tar.gz              0
+  19    DateTime::Format::Builder      DateTime-Format-Builder-0.7807.tar.gz    0
+  20    ExtUtils::Manifest             ExtUtils-Manifest-1.51.tar.gz            0
+  21    URI                            URI-1.35.tar.gz                          1.30
+  22    Data::Validate::Domain         Data-Validate-Domain-0.05.tar.gz         0
+  23    Test::Exception                Test-Exception-0.24.tar.gz               0
+  24    Tree::DAG_Node                 Tree-DAG_Node-1.05.tar.gz                0
+  25    Array::Compare                 Array-Compare-1.13.tar.gz                0
+  26    HTML::Parser                   HTML-Parser-3.56.tar.gz                  0
+  27    Digest::MD5                    Digest-MD5-2.36.tar.gz                   0
+  28    Net::FTP                       libnet-1.19.tar.gz                       0
+  29    Compress::Zlib                 Compress-Zlib-2.003.tar.gz               0
+  30    Locale::Maketext::Simple       Locale-Maketext-Simple-0.18.tar.gz       0
+  31    Parse::RecDescent              Parse-RecDescent-1.94.tar.gz             1.94
+  32    Class::Accessor                Class-Accessor-0.30.tar.gz               0.19
+  33    DateTime::Format::ISO8601      DateTime-Format-ISO8601-0.06.tar.gz      0.06
+  34    CGI                            CGI.pm-3.25.tar.gz                       3
+  35    Test::Cmd                      Test-Cmd-1.05.tar.gz                     1.05
+  36    Net::HTTPServer                Net-HTTPServer-1.1.1.tar.gz              1.1.1
+  37    LWP                            libwww-perl-5.805.tar.gz                 0
+  38    Digest::MD5::File              Digest-MD5-File-0.05.tar.gz              0.03
+  39    File::Temp                     File-Temp-0.18.tar.gz                    0.16
+  40    Data::Validate::URI            Data-Validate-URI-0.01.tar.gz            0.01
+  41    Test::Warn                     Test-Warn-0.08.tar.gz                    0
+  42    YAML                           YAML-0.62.tar.gz                         0.58           y
+  43    Module::Load                   Module-Load-0.10.tar.gz                  0
+  44    Params::Check                  Params-Check-0.25.tar.gz                 0
+  45    Template                       Template-Toolkit-2.16.tar.gz             0              n,n
+  46    Statistics::Descriptive        Statistics-Descriptive-2.6.tar.gz        2.6
+  47    Storable                       Storable-2.15.tar.gz                     0
+  48    IO::String                     IO-String-1.08.tar.gz                    0
+  49    Date::Parse                    TimeDate-1.16.tar.gz                     0
+  50    Digest::SHA1                   Digest-SHA1-2.11.tar.gz                  0
+  51    DB_File                        DB_File-1.814.tar.gz                     0
+  52    File::NFSLock                  File-NFSLock-1.20.tar.gz                 0
+  53    Heap                           Heap-0.71.tar.gz                         0
+  54    Module::Load::Conditional      Module-Load-Conditional-0.16.tar.gz      0
+  55    IPC::Run                       IPC-Run-0.80.tar.gz                      0
+  56    Cache                          Cache-2.04.tar.gz                        0
+  57    IPC::Cmd                       IPC-Cmd-0.36.tar.gz                      0.36
+  58    SOAP::Lite                     SOAP-Lite-0.69.tar.gz                    0              yes,yes,no
+  59    Log::Log4perl                  Log-Log4perl-1.10.tar.gz                 0
+# 60    File::ExtAttr                  File-ExtAttr-1.04.tar.gz                 0
+  61    Text::Glob                     Text-Glob-0.08.tar.gz                    0.08
+  62    Number::Compare                Number-Compare-0.01.tar.gz               0.01
+  63    File::Find::Rule               File-Find-Rule-0.30.tar.gz               0.30
+  64    Astro::FITS::CFITSIO           Astro-FITS-CFITSIO-1.05.tar.gz           0
+  65    Test::More                     Test-Simple-0.74.tar.gz                  0.49
+#  66    Apache::DBI                   Apache-DBI-1.06.tar.gz                   0
+#  67    Apache2::SOAP                 Apache2-SOAP-0.72.tar.gz                 0
+  68    Test::URI                      Test-URI-1.08.tar.gz                     0
+#  69    Sys::Statistics::Linux::DiskUsage Sys-Statistics-Linux-0.26.tar.gz      0
+#  70    Config::YAML                  Config-YAML-1.42.tar.gz                  0
+#  72    File::ExtAttr                 File-ExtAttr-1.07.tar.gz                 0
+  73    DBI                            DBI-1.601.tar.gz                         0
+  71    DBD::mysql                     DBD-mysql-4.006.tar.gz                   0
+#  74    Net::Server::Daemonize                Net-Server-0.97.tar.gz                   0.05
+  75    File::Path                      File-Path-2.04.tar.gz
+  76    File::Mountpoint                File-Mountpoint-0.01.tar.gz             0.01
+  77    Filesys::Df                     Filesys-Df-0.92.tar.gz                  0.92
+  78    SQL::Interp                     SQL-Interp-1.06.tar.gz
+
Index: /branches/cnb_branches/cnb_branch_20090301/psphot/src/Makefile.am
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psphot/src/Makefile.am	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/psphot/src/Makefile.am	(revision 23352)
@@ -1,4 +1,14 @@
 lib_LTLIBRARIES = libpsphot.la
-libpsphot_la_CFLAGS = $(PSPHOT_CFLAGS) $(PSMODULE_CFLAGS) $(PSLIB_CFLAGS)
+
+# PSPHOT_VERSION=`if [ -e ../../VERSION ]; then cat ../../VERSION; else svnversion; fi`
+# PSPHOT_BRANCH=`if [ -e ../../BRANCH ]; then cat ../../BRANCH; else svn info | sed -n -e '/URL:/ h' -e '/Repository Root:/ { x; H; x; s|Repository Root: \(.*\)\nURL: \1\(.*\)|\2| ; s|^/|| ; s|/[a-zA-Z]*/src.*|| ; p }'; fi`
+# PSPHOT_SOURCE=`if [ -e ../../SOURCE ]; then cat ../../SOURCE; else svn info | sed -n -e 's/Repository UUID: // p'; fi`
+# 
+# # Force recompilation of psphotVersion.c, since it gets the version information
+# psphotVersion.c: FORCE
+# 	touch psphotVersion.c
+# FORCE: ;
+
+libpsphot_la_CFLAGS = $(PSPHOT_CFLAGS) $(PSMODULE_CFLAGS) $(PSLIB_CFLAGS) -DPSPHOT_VERSION=$(PSPHOT_VERSION) -DPSPHOT_BRANCH=$(PSPHOT_BRANCH) -DPSPHOT_SOURCE=$(SVN_SOURCE)
 libpsphot_la_LDFLAGS = $(PSPHOT_LIBS) $(PSMODULE_LIBS) $(PSLIB_LIBS)
 
Index: /branches/cnb_branches/cnb_branch_20090301/psphot/src/psphot.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psphot/src/psphot.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/psphot/src/psphot.c	(revision 23352)
@@ -9,5 +9,5 @@
 
     psTimerStart ("complete");
-    pmErrorRegister();			// register psModule's error codes/messages
+    pmErrorRegister();                  // register psModule's error codes/messages
     psphotInit();
 
@@ -15,7 +15,9 @@
     pmConfig *config = psphotArguments (argc, argv);
     if (!config) {
-	psErrorStackPrint(stderr, "Error reading arguments\n");
-	usage ();
+        psErrorStackPrint(stderr, "Error reading arguments\n");
+        usage ();
     }
+
+    psphotVersionPrint();
 
     // load input data (config and images (signal, noise, mask)
Index: /branches/cnb_branches/cnb_branch_20090301/psphot/src/psphot.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psphot/src/psphot.h	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/psphot/src/psphot.h	(revision 23352)
@@ -16,5 +16,9 @@
 const char     *psphotCVSName(void);
 psString        psphotVersion(void);
+psString        psphotSource(void);
 psString        psphotVersionLong(void);
+bool            psphotVersionHeader(psMetadata *header);
+bool            psphotVersionHeaderFull(psMetadata *header);
+void            psphotVersionPrint(void);
 
 bool            psphotModelTest (pmConfig *config, const pmFPAview *view, psMetadata *recipe);
@@ -167,5 +171,4 @@
 
 // psphotVisual functions
-bool psphotSetVisual (bool mode);
 bool psphotVisualShowImage (pmReadout *readout);
 bool psphotVisualShowBackground (pmConfig *config, const pmFPAview *view, pmReadout *readout);
Index: /branches/cnb_branches/cnb_branch_20090301/psphot/src/psphotArguments.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psphot/src/psphotArguments.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/psphot/src/psphotArguments.c	(revision 23352)
@@ -82,6 +82,5 @@
     if ((N = psArgumentGet (argc, argv, "-visual"))) {
         psArgumentRemove (N, &argc, argv);
-        psphotSetVisual (true);
-        // pmSourceSetVisual (true);
+        pmVisualSetVisual(true);
     }
 
Index: /branches/cnb_branches/cnb_branch_20090301/psphot/src/psphotImageLoop.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psphot/src/psphotImageLoop.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/psphot/src/psphotImageLoop.c	(revision 23352)
@@ -26,4 +26,5 @@
 
     pmFPAview *view = pmFPAviewAlloc (0);
+    pmHDU *lastHDU = NULL;              // Last HDU updated
 
     // files associated with the science image
@@ -64,4 +65,13 @@
                 psLogMsg ("psphot", 6, "Cell %d: %x %x\n", view->cell, cell->file_exists, cell->process);
                 if (! readout->data_exists) { continue; }
+
+                // Update the header
+                {
+                    pmHDU *hdu = pmHDUGetHighest(input->fpa, chip, cell);
+                    if (hdu && hdu != lastHDU) {
+                        psphotVersionHeaderFull(hdu->header);
+                        lastHDU = hdu;
+                    }
+                }
 
                 // run the actual photometry analysis on this chip/cell/readout
Index: /branches/cnb_branches/cnb_branch_20090301/psphot/src/psphotMagnitudes.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psphot/src/psphotMagnitudes.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/psphot/src/psphotMagnitudes.c	(revision 23352)
@@ -15,5 +15,5 @@
     int nThreads = psMetadataLookupS32(&status, config->arguments, "NTHREADS"); // Number of threads
     if (!status) {
-	nThreads = 0;
+        nThreads = 0;
     }
 
@@ -36,5 +36,8 @@
 
     // the binning details are saved on the analysis metadata
-    psImageBinning *binning = psMetadataLookupPtr(&status, recipe, "PSPHOT.BACKGROUND.BINNING");
+    psImageBinning *binning = NULL;
+    if (backModel) {
+        binning = psMetadataLookupPtr(&status, backModel->analysis, "PSPHOT.BACKGROUND.BINNING");
+    }
 
     bool IGNORE_GROWTH = psMetadataLookupBool (&status, recipe, "IGNORE_GROWTH");
@@ -53,55 +56,55 @@
     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_MAGNITUDES");
-
-	    psArrayAdd(job->args, 1, cells->data[j]); // sources
-	    psArrayAdd(job->args, 1, psf);
-	    psArrayAdd(job->args, 1, binning);
-	    psArrayAdd(job->args, 1, backModel);
-	    psArrayAdd(job->args, 1, backStdev);
-
-	    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, 0,        PS_TYPE_S32); // this is used as a return value for nAp
-
-	    if (!psThreadJobAddPending(job)) {
-		psError(PS_ERR_UNKNOWN, false, "Unable to guess model.");
-		psFree (job);
-		return false;
-	    }
-	    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_MAGNITUDES");
+
+            psArrayAdd(job->args, 1, cells->data[j]); // sources
+            psArrayAdd(job->args, 1, psf);
+            psArrayAdd(job->args, 1, binning);
+            psArrayAdd(job->args, 1, backModel);
+            psArrayAdd(job->args, 1, backStdev);
+
+            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, 0,        PS_TYPE_S32); // this is used as a return value for nAp
+
+            if (!psThreadJobAddPending(job)) {
+                psError(PS_ERR_UNKNOWN, false, "Unable to guess model.");
+                psFree (job);
+                return false;
+            }
+            psFree(job);
 
 # if (0)
-		int nap = 0;
-		if (!psphotMagnitudes_Unthreaded (&nap, cells->data[j], psf, binning, backModel, backStdev, photMode, maskVal)) {
-		    psError(PS_ERR_UNKNOWN, false, "Unable to guess model.");
-		    return false;
-		}
-		Nap += nap;
+                int nap = 0;
+                if (!psphotMagnitudes_Unthreaded (&nap, cells->data[j], psf, binning, backModel, backStdev, photMode, maskVal)) {
+                    psError(PS_ERR_UNKNOWN, false, "Unable to guess model.");
+                    return false;
+                }
+                Nap += nap;
 # endif
-	}
-
-	// 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 = job->args->data[7];
-		Nap += scalar->data.S32;
-	    }
-	    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 = job->args->data[7];
+                Nap += scalar->data.S32;
+            }
+            psFree(job);
+        }
     }
 
@@ -130,23 +133,23 @@
         if (status && isfinite(source->apMag)) Nap ++;
 
-	if (backModel) {
-	    psAssert (binning, "if backModel is defined, so should binning be");
-	    source->sky = psImageUnbinPixel(source->peak->x, source->peak->y, backModel->image, binning);
-	    if (isnan(source->sky) && false) {
-		psLogMsg ("psphot.magnitudes", PS_LOG_DETAIL, "error setting pmSource.sky");
-	    }
-	} else {
-	    source->sky = NAN;
-	}
-
-	if (backStdev) {
-	    psAssert (binning, "if backStdev is defined, so should binning be");
-	    source->skyErr = psImageUnbinPixel(source->peak->x, source->peak->y, backStdev->image, binning);
-	    if (isnan(source->skyErr) && false) {
-		psLogMsg ("psphot.magnitudes", PS_LOG_DETAIL, "error setting pmSource.skyErr");
-	    }
-	} else {
-	    source->skyErr = NAN;
-	}
+        if (backModel) {
+            psAssert (binning, "if backModel is defined, so should binning be");
+            source->sky = psImageUnbinPixel(source->peak->x, source->peak->y, backModel->image, binning);
+            if (isnan(source->sky) && false) {
+                psLogMsg ("psphot.magnitudes", PS_LOG_DETAIL, "error setting pmSource.sky");
+            }
+        } else {
+            source->sky = NAN;
+        }
+
+        if (backStdev) {
+            psAssert (binning, "if backStdev is defined, so should binning be");
+            source->skyErr = psImageUnbinPixel(source->peak->x, source->peak->y, backStdev->image, binning);
+            if (isnan(source->skyErr) && false) {
+                psLogMsg ("psphot.magnitudes", PS_LOG_DETAIL, "error setting pmSource.skyErr");
+            }
+        } else {
+            source->skyErr = NAN;
+        }
     }
 
@@ -169,23 +172,23 @@
         if (status && isfinite(source->apMag)) Nap ++;
 
-	if (backModel) {
-	    psAssert (binning, "if backModel is defined, so should binning be");
-	    source->sky = psImageUnbinPixel(source->peak->x, source->peak->y, backModel->image, binning);
-	    if (isnan(source->sky) && false) {
-		psLogMsg ("psphot.magnitudes", PS_LOG_DETAIL, "error setting pmSource.sky");
-	    }
-	} else {
-	    source->sky = NAN;
-	}
-
-	if (backStdev) {
-	    psAssert (binning, "if backStdev is defined, so should binning be");
-	    source->skyErr = psImageUnbinPixel(source->peak->x, source->peak->y, backStdev->image, binning);
-	    if (isnan(source->skyErr) && false) {
-		psLogMsg ("psphot.magnitudes", PS_LOG_DETAIL, "error setting pmSource.skyErr");
-	    }
-	} else {
-	    source->skyErr = NAN;
-	}
+        if (backModel) {
+            psAssert (binning, "if backModel is defined, so should binning be");
+            source->sky = psImageUnbinPixel(source->peak->x, source->peak->y, backModel->image, binning);
+            if (isnan(source->sky) && false) {
+                psLogMsg ("psphot.magnitudes", PS_LOG_DETAIL, "error setting pmSource.sky");
+            }
+        } else {
+            source->sky = NAN;
+        }
+
+        if (backStdev) {
+            psAssert (binning, "if backStdev is defined, so should binning be");
+            source->skyErr = psImageUnbinPixel(source->peak->x, source->peak->y, backStdev->image, binning);
+            if (isnan(source->skyErr) && false) {
+                psLogMsg ("psphot.magnitudes", PS_LOG_DETAIL, "error setting pmSource.skyErr");
+            }
+        } else {
+            source->skyErr = NAN;
+        }
     }
 
@@ -210,5 +213,5 @@
     int nThreads = psMetadataLookupS32(&status, config->arguments, "NTHREADS"); // Number of threads
     if (!status) {
-	nThreads = 0;
+        nThreads = 0;
     }
     nThreads = 0; // XXX until testing is complete, do not thread this function
@@ -233,37 +236,37 @@
     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_PSF_WEIGHTS");
-
-	    psArrayAdd(job->args, 1, cells->data[j]); // sources
-	    PS_ARRAY_ADD_SCALAR(job->args, maskVal,  PS_TYPE_IMAGE_MASK);
-
-	    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");
-	    } 
-	    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_PSF_WEIGHTS");
+
+            psArrayAdd(job->args, 1, cells->data[j]); // sources
+            PS_ARRAY_ADD_SCALAR(job->args, maskVal,  PS_TYPE_IMAGE_MASK);
+
+            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");
+            }
+            psFree(job);
+        }
     }
 
@@ -285,21 +288,21 @@
         pmSource *source = (pmSource *) sources->data[i];
 
-	// we must have a valid model
-	pmModel *model = pmSourceGetModel (&isPSF, source);
-	if (model == NULL) {
-	  psTrace ("psphot", 3, "fail mag : no valid model");
-	  source->pixWeight = NAN;
-	  continue;
-	}
+        // we must have a valid model
+        pmModel *model = pmSourceGetModel (&isPSF, source);
+        if (model == NULL) {
+          psTrace ("psphot", 3, "fail mag : no valid model");
+          source->pixWeight = NAN;
+          continue;
+        }
 
         status = pmSourcePixelWeight (&source->pixWeight, model, source->maskObj, maskVal);
-	if (!status) {
-	  psTrace ("psphot", 3, "fail to measure pixel weight");
-	  source->pixWeight = NAN;
-	  continue;
-	}
-
-    }
-
-    return true;
-}
+        if (!status) {
+          psTrace ("psphot", 3, "fail to measure pixel weight");
+          source->pixWeight = NAN;
+          continue;
+        }
+
+    }
+
+    return true;
+}
Index: /branches/cnb_branches/cnb_branch_20090301/psphot/src/psphotMaskReadout.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psphot/src/psphotMaskReadout.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/psphot/src/psphotMaskReadout.c	(revision 23352)
@@ -29,4 +29,17 @@
     }
 
+    // make this an option via the recipe
+    if (0) {
+      psImage *im = readout->image;
+      psImage *wt = readout->variance;
+      psImage *mk = readout->mask;
+      for (int j = 0; j < im->numRows; j++) {
+	for (int i = 0; i < im->numCols; i++) {
+	  if (isfinite(im->data.F32[j][i]) && isfinite(wt->data.F32[j][i])) continue;
+	  mk->data.PS_TYPE_IMAGE_MASK_DATA[j][i] |= maskBad;
+	}
+      }
+    }
+
     // mask the excluded outer pixels
     // these coordinates refer to the parent image
Index: /branches/cnb_branches/cnb_branch_20090301/psphot/src/psphotModelBackground.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psphot/src/psphotModelBackground.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/psphot/src/psphotModelBackground.c	(revision 23352)
@@ -31,6 +31,6 @@
 static bool backgroundModel(psImage *model,  // Model image
                             psImage *modelStdev, // Model stdev image
-                            psImage *image, // Image for which to generate a background model
-                            psImage *mask, // Mask for image
+                            psMetadata *analysis, // Analysis metadata for outputs
+                            pmReadout *readout, // Readout for which to generate a background model
                             psImageBinning *binning, // Binning parameters
                             const pmConfig *config // Configuration
@@ -41,4 +41,6 @@
     bool status = true;
 
+    psImage *image = readout->image, *mask = readout->mask; // Image and mask for readout
+
     // select the appropriate recipe information
     psMetadata *recipe  = psMetadataLookupPtr (&status, config->recipes, PSPHOT_RECIPE);
@@ -49,10 +51,5 @@
     assert (maskVal);
 
-    // user supplied seed, if available
-    unsigned long seed = psMetadataLookupS32 (&status, recipe, "IMSTATS_SEED");
-    if (!status) {
-        seed = 0;
-    }
-    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, seed);
+    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS);
 
     // subtract this amount extra from the sky
@@ -140,7 +137,6 @@
 
     // we save the binning structure for use in psphotMagnitudes
-    status = psMetadataAddPtr(recipe, PS_LIST_TAIL, "PSPHOT.BACKGROUND.BINNING", PS_DATA_UNKNOWN | PS_META_REPLACE, "Background binning", binning);
-    PS_ASSERT (status, false);
-
+    psMetadataAddPtr(analysis, PS_LIST_TAIL, "PSPHOT.BACKGROUND.BINNING",
+                     PS_DATA_UNKNOWN | PS_META_REPLACE, "Background binning", binning);
 
     psF32 **modelData = model->data.F32;
@@ -343,5 +339,5 @@
     psImage *modelStdev = psImageAlloc(binning->nXruff, binning->nYruff, PS_TYPE_F32); // Standard deviation
 
-    if (!backgroundModel(model, modelStdev, ro->image, ro->mask, binning, config)) {
+    if (!backgroundModel(model, modelStdev, ro->analysis, ro, binning, config)) {
         psFree(model);
         psFree(modelStdev);
@@ -365,13 +361,11 @@
     pmFPAfile *file = psMetadataLookupPtr (&status, config->files, filename);
     pmFPA *inFPA = file->fpa;
-    pmReadout *readout = pmFPAviewThisReadout (view, inFPA);
-    psImage *image = readout->image;
-    psImage *mask  = readout->mask;
-
-    psImageBinning *binning = backgroundBinning(image, config); // Image binning parameters
+    pmReadout *readout = pmFPAviewThisReadout(view, inFPA);
+
+    psImageBinning *binning = backgroundBinning(readout->image, config); // Image binning parameters
     pmReadout *model = pmFPAGenerateReadout(config, view, "PSPHOT.BACKMDL", inFPA, binning);
     pmReadout *modelStdev = pmFPAGenerateReadout(config, view, "PSPHOT.BACKMDL.STDEV", inFPA, binning);
 
-    if (!backgroundModel(model->image, modelStdev->image, image, mask, binning, config)) {
+    if (!backgroundModel(model->image, modelStdev->image, model->analysis, readout, binning, config)) {
         psError(PS_ERR_UNKNOWN, false, "Unable to generate background model");
         return false;
Index: /branches/cnb_branches/cnb_branch_20090301/psphot/src/psphotSetThreads.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psphot/src/psphotSetThreads.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/psphot/src/psphotSetThreads.c	(revision 23352)
@@ -25,5 +25,5 @@
     psFree(task);
 
-    task = psThreadTaskAlloc("PSPHOT_SOURCE_STATS", 4);
+    task = psThreadTaskAlloc("PSPHOT_SOURCE_STATS", 5);
     task->function = &psphotSourceStats_Threaded;
     psThreadTaskAdd(task);
Index: /branches/cnb_branches/cnb_branch_20090301/psphot/src/psphotSourceStats.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psphot/src/psphotSourceStats.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/psphot/src/psphotSourceStats.c	(revision 23352)
@@ -15,5 +15,5 @@
     int nThreads = psMetadataLookupS32(&status, config->arguments, "NTHREADS"); // Number of threads
     if (!status) {
-	nThreads = 0;
+        nThreads = 0;
     }
 
@@ -41,8 +41,8 @@
         pmSource *source = pmSourceAlloc();
 
-	// add the peak
+        // add the peak
         source->peak = psMemIncrRefCounter(peak);
 
-	// allocate space for moments
+        // allocate space for moments
         source->moments = pmMomentsAlloc();
 
@@ -50,14 +50,14 @@
         pmSourceDefinePixels (source, readout, source->peak->x, source->peak->y, OUTER);
 
-	peak->assigned = true;
-	psArrayAdd (sources, 100, source);
-	psFree (source);
+        peak->assigned = true;
+        psArrayAdd (sources, 100, source);
+        psFree (source);
     }
 
     if (!strcasecmp (breakPt, "PEAKS")) {
-	psLogMsg ("psphot", PS_LOG_INFO, "%ld sources : %f sec\n", sources->n, psTimerMark ("psphot.stats"));
-	psLogMsg ("psphot", PS_LOG_INFO, "break point PEAKS, skipping MOMENTS\n");
-	psphotVisualShowMoments (sources);
-	return sources;
+        psLogMsg ("psphot", PS_LOG_INFO, "%ld sources : %f sec\n", sources->n, psTimerMark ("psphot.stats"));
+        psLogMsg ("psphot", PS_LOG_INFO, "break point PEAKS, skipping MOMENTS\n");
+        psphotVisualShowMoments (sources);
+        return sources;
     }
 
@@ -65,4 +65,5 @@
     int Nfail = 0;
     int Nmoments = 0;
+    int Nfaint = 0;
 
     // choose Cx, Cy (see psphotThreadTools.c for overview of the concepts)
@@ -74,60 +75,63 @@
     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_SOURCE_STATS");
-
-	    psArrayAdd(job->args, 1, cells->data[j]); // sources
-	    psArrayAdd(job->args, 1, recipe);
-	    PS_ARRAY_ADD_SCALAR(job->args, 0, PS_TYPE_S32); // this is used as a return value for Nmoments
-	    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);
+        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_SOURCE_STATS");
+
+            psArrayAdd(job->args, 1, cells->data[j]); // sources
+            psArrayAdd(job->args, 1, recipe);
+            PS_ARRAY_ADD_SCALAR(job->args, 0, PS_TYPE_S32); // this is used as a return value for Nmoments
+            PS_ARRAY_ADD_SCALAR(job->args, 0, PS_TYPE_S32); // this is used as a return value for Nfail
+            PS_ARRAY_ADD_SCALAR(job->args, 0, PS_TYPE_S32); // this is used as a return value for Nfaint
+
+            if (!psThreadJobAddPending(job)) {
+                psError(PS_ERR_UNKNOWN, false, "Unable to guess model.");
+                psFree (job);
+                return NULL;
+            }
+            psFree(job);
 
 # if (0)
-		int nfail = 0;
-		int nmoments = 0;
-		if (!psphotSourceStats_Unthreaded (&nfail, &nmoments, cells->data[j], recipe)) {
-		    psError(PS_ERR_UNKNOWN, false, "Unable to guess model.");
-		    return NULL;
-		}
-		Nfail += nfail;
-		Nmoments += nmoments;
+                int nfail = 0;
+                int nmoments = 0;
+                if (!psphotSourceStats_Unthreaded (&nfail, &nmoments, cells->data[j], recipe)) {
+                    psError(PS_ERR_UNKNOWN, false, "Unable to guess model.");
+                    return NULL;
+                }
+                Nfail += nfail;
+                Nmoments += nmoments;
 # endif
-	}
-
-	// 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[2];
-		Nmoments += scalar->data.S32;
-		scalar = job->args->data[3];
-		Nfail += scalar->data.S32;
-	    }
-	    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[2];
+                Nmoments += scalar->data.S32;
+                scalar = job->args->data[3];
+                Nfail += scalar->data.S32;
+                scalar = job->args->data[4];
+                Nfaint += scalar->data.S32;
+            }
+            psFree(job);
+        }
     }
 
     psFree (cellGroups);
 
-    psLogMsg ("psphot", PS_LOG_INFO, "%ld sources, %d moments, %d failed: %f sec\n", sources->n, Nmoments, Nfail, psTimerMark ("psphot.stats"));
+    psLogMsg ("psphot", PS_LOG_INFO, "%ld sources, %d moments, %d faint, %d failed: %f sec\n", sources->n, Nmoments, Nfaint, Nfail, psTimerMark ("psphot.stats"));
 
     psphotVisualShowMoments (sources);
@@ -166,4 +170,5 @@
     int Nfail = 0;
     int Nmoments = 0;
+    int Nfaint = 0;
     for (int i = 0; i < sources->n; i++) {
         pmSource *source = sources->data[i];
@@ -171,5 +176,6 @@
         // skip faint sources for moments measurement
         if (source->peak->SN < MIN_SN) {
-	    source->mode |= PM_SOURCE_MODE_BELOW_MOMENTS_SN;
+            source->mode |= PM_SOURCE_MODE_BELOW_MOMENTS_SN;
+            Nfaint++;
             continue;
         }
@@ -179,8 +185,8 @@
         status = pmSourceLocalSky (source, PS_STAT_SAMPLE_MEDIAN, INNER, maskVal, markVal);
         if (!status) {
-	    source->mode |= PM_SOURCE_MODE_SKY_FAILURE;
-	    psErrorClear(); // XXX re-consider the errors raised here
-	    Nfail ++;
-	    continue;
+            source->mode |= PM_SOURCE_MODE_SKY_FAILURE;
+            psErrorClear(); // XXX re-consider the errors raised here
+            Nfail ++;
+            continue;
         }
 
@@ -189,8 +195,8 @@
         status = pmSourceLocalSkyVariance (source, PS_STAT_SAMPLE_MEDIAN, INNER, maskVal, markVal);
         if (!status) {
-	    source->mode |= PM_SOURCE_MODE_SKYVAR_FAILURE;
-	    Nfail ++;
-	    psErrorClear();
-	    continue;
+            source->mode |= PM_SOURCE_MODE_SKYVAR_FAILURE;
+            Nfail ++;
+            psErrorClear();
+            continue;
         }
 
@@ -208,10 +214,10 @@
         status = pmSourceMoments (source, BIG_RADIUS);
         if (status) {
-	    source->mode |= PM_SOURCE_MODE_BIG_RADIUS;
+            source->mode |= PM_SOURCE_MODE_BIG_RADIUS;
             Nmoments ++;
             continue;
         }
 
-	source->mode |= PM_SOURCE_MODE_MOMENTS_FAILURE;
+        source->mode |= PM_SOURCE_MODE_MOMENTS_FAILURE;
         Nfail ++;
         psErrorClear();
@@ -225,5 +231,8 @@
     scalar = job->args->data[3];
     scalar->data.S32 = Nfail;
-    
+
+    scalar = job->args->data[4];
+    scalar->data.S32 = Nfaint;
+
     return true;
 }
@@ -268,7 +277,7 @@
         status = pmSourceLocalSky (source, PS_STAT_SAMPLE_MEDIAN, INNER, maskVal, markVal);
         if (!status) {
-	    psErrorClear(); // XXX re-consider the errors raised here
-	    Nfail ++;
-	    continue;
+            psErrorClear(); // XXX re-consider the errors raised here
+            Nfail ++;
+            continue;
         }
 
@@ -277,7 +286,7 @@
         status = pmSourceLocalSkyVariance (source, PS_STAT_SAMPLE_MEDIAN, INNER, maskVal, markVal);
         if (!status) {
-	    Nfail ++;
-	    psErrorClear();
-	    continue;
+            Nfail ++;
+            psErrorClear();
+            continue;
         }
 
@@ -307,6 +316,6 @@
     *nmoments = Nmoments;
     *nfail = Nfail;
-    
+
     return true;
 }
-# endif 
+# endif
Index: /branches/cnb_branches/cnb_branch_20090301/psphot/src/psphotSubtractBackground.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psphot/src/psphotSubtractBackground.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/psphot/src/psphotSubtractBackground.c	(revision 23352)
@@ -4,5 +4,5 @@
 // generate the median in NxN boxes, clipping heavily
 // linear interpolation to generate full-scale model
-bool psphotSubtractBackground (pmConfig *config, const pmFPAview *view, const char *filename) 
+bool psphotSubtractBackground (pmConfig *config, const pmFPAview *view, const char *filename)
 {
     bool status = true;
@@ -31,5 +31,5 @@
     assert (maskVal);
 
-    psImageBinning *binning = psMetadataLookupPtr(&status, recipe, "PSPHOT.BACKGROUND.BINNING");
+    psImageBinning *binning = psMetadataLookupPtr(&status, model->analysis, "PSPHOT.BACKGROUND.BINNING");
     assert (binning);
 
Index: /branches/cnb_branches/cnb_branch_20090301/psphot/src/psphotVersion.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psphot/src/psphotVersion.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/psphot/src/psphotVersion.c	(revision 23352)
@@ -1,40 +1,128 @@
-# include "psphotInternal.h"
+#include "psphotInternal.h"
 
-# if (HAVE_KAPA)
-# include <kapa.h>
-# endif
+#ifdef HAVE_KAPA
+#include <kapa.h>
+#endif
 
-static const char *cvsTag = "$Name: not supported by cvs2svn $";// CVS tag name
+#ifndef PSPHOT_VERSION
+#error "PSPHOT_VERSION is not set"
+#endif
+#ifndef PSPHOT_BRANCH
+#error "PSPHOT_BRANCH is not set"
+#endif
+#ifndef PSPHOT_SOURCE
+#error "PSPHOT_SOURCE is not set"
+#endif
+
+#define xstr(s) str(s)
+#define str(s) #s
 
 psString psphotVersion(void)
 {
-    psString version = NULL;            // Version, to return
-    psStringAppend(&version, "%s-%s",PACKAGE_NAME,PACKAGE_VERSION);
-    return version;
+    char *value = NULL;
+    psStringAppend(&value, "%s@%s", xstr(PSPHOT_BRANCH), xstr(PSPHOT_VERSION));
+    return value;
+}
+
+psString psphotSource(void)
+{
+    return psStringCopy(xstr(PSPHOT_SOURCE));
 }
 
 psString psphotVersionLong(void)
 {
-    psString version = psphotVersion(); // Version, to return
-    psString tag = psStringStripCVS(cvsTag, "Name"); // CVS tag
-    psStringAppend(&version, " (cvs tag %s) %s, %s", tag, __DATE__, __TIME__);
+    psString version = psLibVersion();  // Version, to return
+    psString source = psLibSource();    // Source
 
-# if (HAVE_KAPA)
-    psString ohanaVersion = psStringStripCVS (ohana_version(), "Name");
-    psString libdvoVersion = psStringStripCVS (libdvo_version(), "Name");
+    psStringPrepend(&version, "psphot ");
+    psStringAppend(&version, " from %s, built %s, %s", source, __DATE__, __TIME__);
+    psFree(source);
 
-    psStringAppend (&version, " with libkapa (ohana %s, libdvo: %s)", ohanaVersion, libdvoVersion);
-    psFree (ohanaVersion);
-    psFree (libdvoVersion);
-# else
-    psStringAppend (&version, " WITHOUT libkapa");
-# endif
+#ifdef __OPTIMIZE__
+    psStringAppend(&version, " optimised");
+#else
+    psStringAppend(&version, " unoptimised");
+#endif
 
-    psFree(tag);
+#ifdef HAVE_KAPA
+#if 0
+    // XXX Need to get ohana and libdvo versions
+    psString ohanaVersion = psStringStripCVS(ohana_version(), "Name");
+    psString libdvoVersion = psStringStripCVS(libdvo_version(), "Name");
+    psStringAppend(&version, " with libkapa (ohana %s, libdvo: %s)", ohanaVersion, libdvoVersion);
+    psFree(ohanaVersion);
+    psFree(libdvoVersion);
+#else
+    psStringAppend(&version, " with libkapa");
+#endif
+
+#else
+    psStringAppend (&version, " without libkapa");
+#endif
+
     return version;
 }
 
-// Defined by RHL; leaving for backwards compatibility.
-const char *psphotCVSName(void) {
-   return cvsTag;
+bool psphotVersionHeader(psMetadata *header)
+{
+    PS_ASSERT_METADATA_NON_NULL(header, false);
+
+    psString version = psphotVersion(); // Software version
+    psString source = psphotSource();   // Software source
+
+    psStringPrepend(&version, "psphot version: ");
+    psStringPrepend(&source, "psphot source: ");
+
+    psMetadataAddStr(header, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, NULL, version);
+    psMetadataAddStr(header, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, NULL, source);
+
+    psFree(version);
+    psFree(source);
+
+    return true;
 }
+
+
+bool psphotVersionHeaderFull(psMetadata *header)
+{
+    PS_ASSERT_METADATA_NON_NULL(header, false);
+
+    psTime *time = psTimeGetNow(PS_TIME_TAI); // The time now
+    psString timeString = psTimeToISO(time); // The time in an ISO string
+    psFree(time);
+    psString history = NULL;               // History string
+    psStringAppend(&history, "psphot at %s", timeString);
+    psFree(timeString);
+    psMetadataAddStr(header, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, NULL, history);
+    psFree(history);
+
+    psLibVersionHeader(header);
+    psModulesVersionHeader(header);
+    psphotVersionHeader(header);
+
+    return true;
+}
+
+
+void psphotVersionPrint(void)
+{
+    psTime *time = psTimeGetNow(PS_TIME_TAI); // The time now
+    psString timeString = psTimeToISO(time); // The time in an ISO string
+    psFree(time);
+    psLogMsg("psphot", PS_LOG_INFO, "psphot at %s", timeString);
+    psFree(timeString);
+
+    psString pslib = psLibVersionLong();// psLib version
+    psString psmodules = psModulesVersionLong(); // psModules version
+    psString psphot = psphotVersionLong(); // psphot version
+
+    psLogMsg("psphot", PS_LOG_INFO, "%s", pslib);
+    psLogMsg("psphot", PS_LOG_INFO, "%s", psmodules);
+    psLogMsg("psphot", PS_LOG_INFO, "%s", psphot);
+
+    psFree(pslib);
+    psFree(psmodules);
+    psFree(psphot);
+
+    return;
+}
Index: /branches/cnb_branches/cnb_branch_20090301/psphot/src/psphotVisual.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psphot/src/psphotVisual.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/psphot/src/psphotVisual.c	(revision 23352)
@@ -18,14 +18,8 @@
 // these are invoked by the -visual options
 
-static bool isVisual = false;
 static int kapa = -1;
 static int kapa2 = -1;
 static int kapa3 = -1;
 
-bool psphotSetVisual (bool mode) {
-
-    isVisual = mode;
-    return true;
-}
 
 bool psphotVisualShowMask (int kapaFD, psImage *inImage, const char *name, int channel) {
@@ -38,5 +32,5 @@
 
     psStats *stats = psStatsAlloc (PS_STAT_ROBUST_MEDIAN | PS_STAT_ROBUST_STDEV);
-    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 0);
+    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS);
     if (!psImageBackground(stats, NULL, inImage, NULL, 0, rng)) {
         fprintf (stderr, "failed to get background values\n");
@@ -84,5 +78,5 @@
 
     psStats *stats = psStatsAlloc (PS_STAT_ROBUST_MEDIAN | PS_STAT_ROBUST_STDEV);
-    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 0);
+    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS);
     if (!psImageBackground(stats, NULL, inImage, NULL, 0, rng)) {
         fprintf (stderr, "failed to get background values\n");
@@ -135,5 +129,5 @@
 bool psphotVisualShowImage (pmReadout *readout) {
 
-    if (!isVisual) return true;
+    if (!pmVisualIsVisual()) return true;
 
     if (kapa == -1) {
@@ -141,5 +135,5 @@
         if (kapa == -1) {
             fprintf (stderr, "failure to open kapa; visual mode disabled\n");
-            isVisual = false;
+            pmVisualSetVisual(false);
             return false;
         }
@@ -164,5 +158,5 @@
     pmReadout *backgnd;
 
-    if (!isVisual) return true;
+    if (!pmVisualIsVisual()) return true;
 
     if (kapa == -1) {
@@ -170,5 +164,5 @@
         if (kapa == -1) {
             fprintf (stderr, "failure to open kapa; visual mode disabled\n");
-            isVisual = false;
+            pmVisualSetVisual(false);
             return false;
         }
@@ -199,5 +193,5 @@
 bool psphotVisualShowSignificance (psImage *image) {
 
-    if (!isVisual) return true;
+    if (!pmVisualIsVisual()) return true;
 
     if (kapa == -1) {
@@ -205,5 +199,5 @@
         if (kapa == -1) {
             fprintf (stderr, "failure to open kapa; visual mode disabled\n");
-            isVisual = false;
+            pmVisualSetVisual(false);
             return false;
         }
@@ -228,5 +222,5 @@
     KiiOverlay *overlay;
 
-    if (!isVisual) return true;
+    if (!pmVisualIsVisual()) return true;
 
     if (kapa == -1) {
@@ -306,5 +300,5 @@
     KiiOverlay *overlay;
 
-    if (!isVisual) return true;
+    if (!pmVisualIsVisual()) return true;
 
     if (kapa == -1) {
@@ -423,5 +417,5 @@
     psEllipseAxes axes;
 
-    if (!isVisual) return true;
+    if (!pmVisualIsVisual()) return true;
 
     if (kapa == -1) {
@@ -478,5 +472,5 @@
     Graphdata graphdata;
 
-    if (!isVisual) return true;
+    if (!pmVisualIsVisual()) return true;
 
     if (kapa3 == -1) {
@@ -484,5 +478,5 @@
         if (kapa3 == -1) {
             fprintf (stderr, "failure to open kapa; visual mode disabled\n");
-            isVisual = false;
+            pmVisualSetVisual(false);
             return false;
         }
@@ -694,5 +688,5 @@
 bool psphotVisualShowRoughClass (psArray *sources) {
 
-    if (!isVisual) return true;
+    if (!pmVisualIsVisual()) return true;
 
     if (kapa == -1) {
@@ -723,5 +717,5 @@
 bool psphotVisualShowPSFModel (pmReadout *readout, pmPSF *psf) {
 
-    if (!isVisual) return true;
+    if (!pmVisualIsVisual()) return true;
 
     if (kapa2 == -1) {
@@ -729,5 +723,5 @@
         if (kapa2 == -1) {
             fprintf (stderr, "failure to open kapa; visual mode disabled\n");
-            isVisual = false;
+            pmVisualSetVisual(false);
             return false;
         }
@@ -801,5 +795,5 @@
     bool status;
 
-    if (!isVisual) return true;
+    if (!pmVisualIsVisual()) return true;
 
     if (kapa2 == -1) {
@@ -807,5 +801,5 @@
         if (kapa2 == -1) {
             fprintf (stderr, "failure to open kapa; visual mode disabled\n");
-            isVisual = false;
+            pmVisualSetVisual(false);
             return false;
         }
@@ -961,5 +955,5 @@
     bool status;
 
-    if (!isVisual) return true;
+    if (!pmVisualIsVisual()) return true;
 
     if (kapa2 == -1) {
@@ -967,5 +961,5 @@
         if (kapa2 == -1) {
             fprintf (stderr, "failure to open kapa; visual mode disabled\n");
-            isVisual = false;
+            pmVisualSetVisual(false);
             return false;
         }
@@ -1214,5 +1208,5 @@
     KapaSection section;  // put the positive profile in one and the residuals in another?
 
-    if (!isVisual) return true;
+    if (!pmVisualIsVisual()) return true;
 
     if (kapa3 == -1) {
@@ -1220,5 +1214,5 @@
         if (kapa3 == -1) {
             fprintf (stderr, "failure to open kapa; visual mode disabled\n");
-            isVisual = false;
+            pmVisualSetVisual(false);
             return false;
         }
@@ -1286,5 +1280,5 @@
     psEllipseAxes axes;
 
-    if (!isVisual) return true;
+    if (!pmVisualIsVisual()) return true;
 
     if (kapa == -1) {
@@ -1387,5 +1381,5 @@
     KiiOverlay *overlay;
 
-    if (!isVisual) return true;
+    if (!pmVisualIsVisual()) return true;
 
     if (kapa == -1) {
@@ -1464,5 +1458,5 @@
     KapaSection section;
 
-    if (!isVisual) return true;
+    if (!pmVisualIsVisual()) return true;
 
     if (kapa3 == -1) {
@@ -1470,5 +1464,5 @@
         if (kapa3 == -1) {
             fprintf (stderr, "failure to open kapa; visual mode disabled\n");
-            isVisual = false;
+            pmVisualSetVisual(false);
             return false;
         }
@@ -1604,5 +1598,5 @@
 bool psphotVisualShowResidualImage (pmReadout *readout) {
 
-    if (!isVisual) return true;
+    if (!pmVisualIsVisual()) return true;
 
     if (kapa == -1) {
@@ -1610,5 +1604,5 @@
         if (kapa == -1) {
             fprintf (stderr, "failure to open kapa; visual mode disabled\n");
-            isVisual = false;
+            pmVisualSetVisual(false);
             return false;
         }
@@ -1631,5 +1625,5 @@
     Graphdata graphdata;
 
-    if (!isVisual) return true;
+    if (!pmVisualIsVisual()) return true;
 
     if (kapa3 == -1) {
@@ -1637,5 +1631,5 @@
         if (kapa3 == -1) {
             fprintf (stderr, "failure to open kapa; visual mode disabled\n");
-            isVisual = false;
+            pmVisualSetVisual(false);
             return false;
         }
@@ -1712,5 +1706,4 @@
 # else
 
-bool psphotSetVisual (bool mode){}
 bool psphotVisualShowImage (pmConfig *config, pmReadout *readout) { return true; }
 bool psphotVisualShowBackground (pmConfig *config, const pmFPAview *view, pmReadout *readout) { return true; }
Index: /branches/cnb_branches/cnb_branch_20090301/pstamp/scripts/pstamp_finish.pl
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/pstamp/scripts/pstamp_finish.pl	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/pstamp/scripts/pstamp_finish.pl	(revision 23352)
@@ -21,6 +21,6 @@
 
 use PS::IPP::Config qw( :standard );
-use PStamp::RequestFile qw( :standard );
-use PStamp::Job qw( :standard );
+use PS::IPP::PStamp::RequestFile qw( :standard );
+use PS::IPP::PStamp::Job qw( :standard );
 
 my ( $req_id, $req_name, $req_file, $out_dir, $product, $dbname, $verbose, $save_temps, $redirect_output);
@@ -105,5 +105,5 @@
     }
 
-    # this function is PStamp::RequestFile::read_request_file
+    # this function is PS::IPP::PStamp::RequestFile::read_request_file
     my ($header, $rows) = read_request_file($req_file);
 
Index: /branches/cnb_branches/cnb_branch_20090301/pstamp/scripts/pstamp_job_run.pl
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/pstamp/scripts/pstamp_job_run.pl	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/pstamp/scripts/pstamp_job_run.pl	(revision 23352)
@@ -13,5 +13,5 @@
 use File::Basename;
 use Digest::MD5::File qw( file_md5_hex );
-use PStamp::RequestFile qw( :standard );
+use PS::IPP::PStamp::RequestFile qw( :standard );
 
 my $verbose;
Index: /branches/cnb_branches/cnb_branch_20090301/pstamp/scripts/pstampparse.pl
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/pstamp/scripts/pstampparse.pl	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/pstamp/scripts/pstampparse.pl	(revision 23352)
@@ -11,6 +11,6 @@
 use Sys::Hostname;
 use Getopt::Long qw( GetOptions );
-use PStamp::RequestFile qw( :standard );
-use PStamp::Job qw( :standard );
+use PS::IPP::PStamp::RequestFile qw( :standard );
+use PS::IPP::PStamp::Job qw( :standard );
 
 my $verbose;
@@ -178,5 +178,5 @@
     }
 
-    # Call PStamp::Job's locate_images routine to get the parameters for this request specification
+    # Call PS::IPP::PStamp::Job's locate_images routine to get the parameters for this request specification
     my $images = locate_images($ipprc, $image_db, $req_type, $img_type, $id, $class_id,
             $x, $y, $mjd_min, $mjd_max, $filter, $verbose);
Index: /branches/cnb_branches/cnb_branch_20090301/pstamp/test/test_lookup.pl
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/pstamp/test/test_lookup.pl	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/pstamp/test/test_lookup.pl	(revision 23352)
@@ -9,5 +9,5 @@
 
 use Getopt::Long qw( GetOptions );
-use PStamp::Job qw( :standard );
+use PS::IPP::PStamp::Job qw( :standard );
 
 use PS::IPP::Config qw( :standard );
Index: /branches/cnb_branches/cnb_branch_20090301/pstest/src/tst_psPolynomial.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/pstest/src/tst_psPolynomial.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/pstest/src/tst_psPolynomial.c	(revision 23352)
@@ -289,5 +289,5 @@
 	    fillVectors(x, f, NORD, NPTS, type[k]);
 	    psTime *now = psTimeGetNow(PS_TIME_UTC);
-	    const psRandom *r = psRandomAlloc(PS_RANDOM_TAUS, now->sec);
+	    const psRandom *r = psRandomAllocSpecific(PS_RANDOM_TAUS, now->sec);
 	    for (int i = 0; i < NPTS; i++) {
 		double errgen = psRandomGaussian(r);
@@ -361,5 +361,5 @@
 	    // add random errors to the vector
 	    psTime *now = psTimeGetNow(PS_TIME_UTC);
-	    const psRandom *r = psRandomAlloc(PS_RANDOM_TAUS, now->sec);
+	    const psRandom *r = psRandomAllocSpecific(PS_RANDOM_TAUS, now->sec);
 	    for (int i = 0; i < NPTS; i++) {
 		double errgen = psRandomGaussian(r);
@@ -464,5 +464,5 @@
 	    fillVectors(x, f, NORD, NPTS, type[k]);
 	    psTime *now = psTimeGetNow(PS_TIME_UTC);
-	    const psRandom *r = psRandomAlloc(PS_RANDOM_TAUS, now->sec);
+	    const psRandom *r = psRandomAllocSpecific(PS_RANDOM_TAUS, now->sec);
 	    for (int i = 0; i < NPTS; i++) {
 		double errgen = psRandomGaussian(r);
Index: /branches/cnb_branches/cnb_branch_20090301/pswarp/src/Makefile.am
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/pswarp/src/Makefile.am	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/pswarp/src/Makefile.am	(revision 23352)
@@ -1,4 +1,14 @@
 bin_PROGRAMS = pswarp
-pswarp_CPPFLAGS = $(PSLIB_CFLAGS) $(PSMODULE_CFLAGS) $(PPSTATS_CFLAGS) $(PSPHOT_CFLAGS) $(PSWARP_CFLAGS)
+
+PSWARP_VERSION=`if [ -e ../../VERSION ]; then cat ../../VERSION; else svnversion; fi`
+PSWARP_BRANCH=`if [ -e ../../BRANCH ]; then cat ../../BRANCH; else svn info | sed -n -e '/URL:/ h' -e '/Repository Root:/ { x; H; x; s|Repository Root: \(.*\)\nURL: \1\(.*\)|\2| ; s|^/|| ; s|/[a-zA-Z]*/src.*|| ; p }'; fi`
+PSWARP_SOURCE=`if [ -e ../../SOURCE ]; then cat ../../SOURCE; else svn info | sed -n -e 's/Repository UUID: // p'; fi`
+
+# Force recompilation of pswarpVersion.c, since it gets the version information
+pswarpVersion.c: FORCE
+	touch pswarpVersion.c
+FORCE: ;
+
+pswarp_CPPFLAGS = $(PSLIB_CFLAGS) $(PSMODULE_CFLAGS) $(PPSTATS_CFLAGS) $(PSPHOT_CFLAGS) $(PSWARP_CFLAGS) -DPSWARP_VERSION=\"$(PSWARP_VERSION)\" -DPSWARP_BRANCH=\"$(PSWARP_BRANCH)\" -DPSWARP_SOURCE=\"$(PSWARP_SOURCE)\"
 pswarp_LDFLAGS = $(PSLIB_LIBS) $(PSMODULE_LIBS) $(PPSTATS_LIBS) $(PSPHOT_LIBS) $(PSWARP_LIBS)
 
@@ -18,5 +28,5 @@
 	pswarpSetThreads.c	        \
 	pswarpTransformReadout.c	\
-	pswarpTransformSources.c \
+	pswarpTransformSources.c 	\
 	pswarpTransformTile.c		\
 	pswarpVersion.c            
Index: /branches/cnb_branches/cnb_branch_20090301/pswarp/src/pswarp.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/pswarp/src/pswarp.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/pswarp/src/pswarp.c	(revision 23352)
@@ -39,4 +39,6 @@
     if (!config) usage();
 
+    pswarpVersionPrint();
+
     // load identify the data sources
     if (!pswarpParseCamera(config)) {
Index: /branches/cnb_branches/cnb_branch_20090301/pswarp/src/pswarp.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/pswarp/src/pswarp.h	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/pswarp/src/pswarp.h	(revision 23352)
@@ -112,2 +112,19 @@
  */
 bool pswarpSetThreads ();
+
+/// Return software version
+psString pswarpVersion(void);
+
+/// Return software souce
+psString pswarpSource(void);
+
+/// Return long software version information
+psString pswarpVersionLong(void);
+
+/// Populate header with version information
+bool pswarpVersionHeader(
+    psMetadata *header                  ///< Header to populate
+    );
+
+/// Print version information
+void pswarpVersionPrint(void);
Index: /branches/cnb_branches/cnb_branch_20090301/pswarp/src/pswarpArguments.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/pswarp/src/pswarpArguments.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/pswarp/src/pswarpArguments.c	(revision 23352)
@@ -41,5 +41,5 @@
         if ((arg = psArgumentGet(argc, argv, "-psphot-visual"))) {
             psArgumentRemove(arg, &argc, argv);
-            psphotSetVisual(true);
+            pmVisualSetVisual(true);
         }
     }
@@ -87,11 +87,6 @@
 
 
-    if (!pmConfigFileSetsMD (config->arguments, &argc, argv, "INPUT", "-file", "-list")) {
-        psError(PSWARP_ERR_ARGUMENTS, true, "Missing -file (input) or -list (input)");
-        return NULL;
-    }
-
-    // the mask and variance entries are optional (build from gain?)
-    pmConfigFileSetsMD (config->arguments, &argc, argv, "MASK",   "-mask",   "-masklist");
+    pmConfigFileSetsMD (config->arguments, &argc, argv, "INPUT", "-file", "-list");
+    pmConfigFileSetsMD (config->arguments, &argc, argv, "MASK", "-mask", "-masklist");
     pmConfigFileSetsMD (config->arguments, &argc, argv, "VARIANCE", "-variance", "-variancelist");
 
@@ -105,14 +100,14 @@
 
     // output position is fixed
-    psMetadataAddStr (config->arguments, PS_LIST_TAIL, "OUTPUT", 0, "", argv[1]);
+    psMetadataAddStr(config->arguments, PS_LIST_TAIL, "OUTPUT", 0, "", argv[1]);
 
     // skycell position is fixed
     array = psArrayAlloc(1);
-    array->data[0] = psStringCopy (argv[2]);
-    status = psMetadataAddPtr (config->arguments, PS_LIST_TAIL, "SKYCELL", PS_DATA_ARRAY, "", array);
-    psFree (array);
+    array->data[0] = psStringCopy(argv[2]);
+    status = psMetadataAddPtr(config->arguments, PS_LIST_TAIL, "SKYCELL", PS_DATA_ARRAY, "", array);
+    psFree(array);
 
     psTrace("pswarp", 1, "Done with pswarpArguments...\n");
-    return (config);
+    return config;
 }
 
@@ -204,16 +199,4 @@
     psTrace("pswarp", 1, "Done with pswarpArguments...\n");
 
-    // Dump configuration, now that's it's settled
-    psString dump_file =  psMetadataLookupStr(&status, config->arguments, "DUMP_CONFIG");
-    if (dump_file) {
-        const char *skyCamera = psMetadataLookupStr(NULL, config->arguments,
-                                                    "SKYCELL.CAMERA");  ///< Name of camera for skycell
-        pmConfigCamerasCull(config, skyCamera);
-        pmConfigRecipesCull(config, "PSWARP,PPSTATS,PSPHOT,MASKS");
-
-        pmFPAfile *input = psMetadataLookupPtr(NULL, config->files, "PSWARP.INPUT"); // Input file
-        pmConfigDump(config, input->fpa, dump_file);
-    }
-
     return (config);
 }
Index: /branches/cnb_branches/cnb_branch_20090301/pswarp/src/pswarpLoop.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/pswarp/src/pswarpLoop.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/pswarp/src/pswarpLoop.c	(revision 23352)
@@ -76,4 +76,10 @@
     bool status;
 
+    const char *skyCamera = psMetadataLookupStr(NULL, config->arguments,
+                                                "SKYCELL.CAMERA");  ///< Name of camera for skycell
+    pmConfigCamerasCull(config, skyCamera);
+    pmConfigRecipesCull(config, "PSWARP,PPSTATS,PSPHOT,MASKS,JPEG");
+
+
     // load the recipe
     psMetadata *recipe = psMetadataLookupPtr (&status, config->recipes, PSWARP_RECIPE);
@@ -81,4 +87,9 @@
         psError(PSPHOT_ERR_CONFIG, false, "missing recipe %s", PSWARP_RECIPE);
         return false;
+    }
+
+    if (!pswarpSetMaskBits(config)) {
+        psError(PS_ERR_IO, false, "failed to set mask bits");
+        return NULL;
     }
 
@@ -339,4 +350,6 @@
     }
 
+    pswarpVersionHeader(hdu->header);
+
     if (!pmAstromWriteWCS(hdu->header, outFPA, outChip, WCS_NONLIN_TOL)) {
         psError(PS_ERR_UNKNOWN, false, "Unable to generate WCS header.");
@@ -455,4 +468,11 @@
     }
 
+    // Dump configuration
+    psString dump_file = psMetadataLookupStr(&status, config->arguments, "DUMP_CONFIG");
+    if (dump_file) {
+        pmFPAfile *input = psMetadataLookupPtr(NULL, config->files, "PSWARP.INPUT"); // Input file
+        pmConfigDump(config, input->fpa, dump_file);
+    }
+
     return true;
 }
Index: /branches/cnb_branches/cnb_branch_20090301/pswarp/src/pswarpParseCamera.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/pswarp/src/pswarpParseCamera.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/pswarp/src/pswarpParseCamera.c	(revision 23352)
@@ -11,62 +11,80 @@
  */
 
-# include "pswarp.h"
+#include "pswarp.h"
+
+// Define an input file
+static pmFPAfile *defineInputFile(pmConfig *config,// Configuration
+                                  pmFPAfile *bind,    // File to which to bind, or NULL
+                                  char *filerule,     // Name of file rule
+                                  char *argname,      // Argument name
+                                  pmFPAfileType fileType // Type of file
+    )
+{
+    bool status;
+
+    // look for the file on the RUN metadata
+    pmFPAfile *file = pmFPAfileDefineFromRun(&status, config, filerule); // File to return
+    if (!status) {
+        psError(PSWARP_ERR_CONFIG, false, "Failed to load file definition for %s", filerule);
+        return NULL;
+    }
+    if (!file) {
+        // look for the file on the argument list
+        if (bind) {
+            file = pmFPAfileBindFromArgs(&status, bind, config, filerule, argname);
+        } else {
+            file = pmFPAfileDefineFromArgs(&status, config, filerule, argname);
+        }
+        if (!status) {
+            psError(PSWARP_ERR_CONFIG, false, "Failed to load file definition for %s", filerule);
+            return false;
+        }
+    }
+
+    if (!file) {
+        return NULL;
+    }
+
+    if (file->type != fileType) {
+        psError(PSWARP_ERR_CONFIG, true, "%s is not of type %s", filerule, pmFPAfileStringFromType(fileType));
+        return NULL;
+    }
+
+    return file;
+}
+
+
+
 
 bool pswarpParseCamera(pmConfig *config)
 {
-    bool status;
-    bool mdok;                          ///< Status of MD lookup
-    pmFPAfile *skycell = NULL;
-    pmConfig *skyConfig = NULL;
+    psAssert(config, "Require configuration");
 
-    // the input image(s) are required arguments; they define the camera
-    status = false;
-    pmFPAfile *input = pmFPAfileDefineFromArgs(&status, config, "PSWARP.INPUT", "INPUT");
-    if (!input || !status) {
+    // The input image(s) is required: it defines the camera
+    pmFPAfile *input = defineInputFile(config, NULL, "PSWARP.INPUT", "INPUT", PM_FPA_FILE_IMAGE);
+    if (!input) {
         psError(PSWARP_ERR_CONFIG, false, "Failed to build FPA from PSWARP.INPUT");
         return false;
     }
 
-    // the input image(s) are required arguments; they define the camera
-    status = false;
-    pmFPAfile *astrom = pmFPAfileDefineFromArgs(&status, config, "PSWARP.ASTROM", "ASTROM");
-    if (!status) {
-        psError (PS_ERR_UNKNOWN, false, "failed to load find definition");
-        return NULL;
-    }
-    if (astrom) {
-        psLogMsg ("pswarp", 3, "using supplied astrometry\n");
-    } else {
-        psLogMsg ("pswarp", 3, "using header astrometry\n");
+    pmFPAfile *astrom = defineInputFile(config, NULL, "PSWARP.ASTROM", "ASTROM", PM_FPA_FILE_CMF);
+    psLogMsg("pswarp", PS_LOG_INFO, "Astrometry source: %s", astrom ? "supplied" : "header");
+
+    pmFPAfile *inMask = defineInputFile(config, input, "PSWARP.MASK", "MASK", PM_FPA_FILE_MASK);
+    if (!inMask) {
+        psLogMsg("pswarp", PS_LOG_INFO, "No mask supplied");
     }
 
-    // the mask is not required - but must conform to input camera
-    pmFPAfile *inMask = pmFPAfileBindFromArgs(&status, input, config, "PSWARP.MASK", "MASK");
-    if (!status) {
-        psError (PS_ERR_UNKNOWN, false, "failed to load find definition");
-        return NULL;
-    }
-    if (!inMask) {
-        psLogMsg ("pswarp", 3, "no mask supplied\n");
+    pmFPAfile *inVariance = defineInputFile(config, input, "PSWARP.VARIANCE", "VARIANCE",
+                                            PM_FPA_FILE_VARIANCE);
+    if (!inVariance) {
+        psLogMsg("pswarp", PS_LOG_INFO, "No variance supplied");
     }
 
-    // loading the mask here should have invoked pmConfigMaskReadHeader()
-    if (!pswarpSetMaskBits (config)) {
-        psError(PS_ERR_IO, false, "failed to set mask bits");
-        return NULL;
-    }
-
-    pmFPAfile *inVariance = pmFPAfileBindFromArgs(&status, input, config, "PSWARP.VARIANCE", "VARIANCE");
-    if (!status) {
-        psError (PS_ERR_UNKNOWN, false, "failed to load find definition");
-        return NULL;
-    }
-    if (!inVariance) {
-        psLogMsg ("pswarp", 3, "no variance supplied\n");
-    }
-
-    // the input skycell is a required argument: it defines the output image
+    // The input skycell is a required argument: it defines the output image
     // XXX we may need a different skycell structure here
-    status = pswarpDefineSkycell(&skycell, &skyConfig, config, "PSWARP.SKYCELL", "SKYCELL");
+    pmFPAfile *skycell = NULL;
+    pmConfig *skyConfig = NULL;
+    bool status = pswarpDefineSkycell(&skycell, &skyConfig, config, "PSWARP.SKYCELL", "SKYCELL");
     if (!status) {
         psError(PSWARP_ERR_CONFIG, false, "Failed to build FPA from PSWARP.SKYCELL");
@@ -81,4 +99,6 @@
         return false;
     }
+    output->save = true;
+
     pmFPAfile *outMask = pmFPAfileDefineSkycell(config, output->fpa, "PSWARP.OUTPUT.MASK");
     if (!outMask) {
@@ -86,6 +106,4 @@
         return false;
     }
-
-    output->save = true;
     outMask->save = true;
 
@@ -108,4 +126,5 @@
     }
 
+    bool mdok;                          // Status of MD lookup
     if (psMetadataLookupBool(&mdok, config->arguments, "PSF")) {
         // This file, PSPHOT.INPUT, is just used as a carrier; output files (eg, PSPHOT.RESID) are defined by
@@ -160,5 +179,5 @@
             }
         }
-        psFree (chips);
+        psFree(chips);
     }
 
Index: /branches/cnb_branches/cnb_branch_20090301/pswarp/src/pswarpVersion.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/pswarp/src/pswarpVersion.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/pswarp/src/pswarpVersion.c	(revision 23352)
@@ -18,22 +18,106 @@
 #include <pslib.h>
 #include <psmodules.h>
+#include <psphot.h>
+#include <ppStats.h>
 #include "pswarp.h"
 
-static const char *cvsTag = "$Name: not supported by cvs2svn $";///< CVS tag name
 
 psString pswarpVersion(void)
 {
-    psString version = NULL;            ///< Version, to return
-    psStringAppend(&version, "%s-%s",PACKAGE_NAME,PACKAGE_VERSION);
-    return version;
+#ifndef PSWARP_VERSION
+#error "PSWARP_VERSION is not set"
+#endif
+#ifndef PSWARP_BRANCH
+#error "PSWARP_BRANCH is not set"
+#endif
+    return psStringCopy(PSWARP_BRANCH "@" PSWARP_VERSION);
+}
+
+psString pswarpSource(void)
+{
+#ifndef PSWARP_SOURCE
+#error "PSWARP_SOURCE is not set"
+#endif
+    return psStringCopy(PSWARP_SOURCE);
 }
 
 psString pswarpVersionLong(void)
 {
-    psString version = pswarpVersion(); ///< Version, to return
-    psString tag = psStringStripCVS(cvsTag, "Name"); ///< CVS tag
-    psStringAppend(&version, " (cvs tag %s) %s, %s", tag, __DATE__, __TIME__);
-    psFree(tag);
+    psString version = pswarpVersion();  // Version, to return
+    psString source = pswarpSource();    // Source
+
+    psStringPrepend(&version, "pswarp ");
+    psStringAppend(&version, " from %s, built %s, %s", source, __DATE__, __TIME__);
+    psFree(source);
+
+#ifdef __OPTIMIZE__
+    psStringAppend(&version, " optimised");
+#else
+    psStringAppend(&version, " unoptimised");
+#endif
+
     return version;
+};
+
+
+bool pswarpVersionHeader(psMetadata *header)
+{
+    PS_ASSERT_METADATA_NON_NULL(header, false);
+
+    psTime *time = psTimeGetNow(PS_TIME_TAI); // The time now
+    psString timeString = psTimeToISO(time); // The time in an ISO string
+    psFree(time);
+    psString history = NULL;               // History string
+    psStringAppend(&history, "pswarp at %s", timeString);
+    psFree(timeString);
+    psMetadataAddStr(header, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, NULL, history);
+    psFree(history);
+
+    psLibVersionHeader(header);
+    psModulesVersionHeader(header);
+    psphotVersionHeader(header);
+    ppStatsVersionHeader(header);
+
+    psString version = pswarpVersion(); // Software version
+    psString source  = pswarpSource();  // Software source
+
+    psStringPrepend(&version, "pswarp version: ");
+    psStringPrepend(&source, "pswarp source: ");
+
+    psMetadataAddStr(header, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, NULL, version);
+    psMetadataAddStr(header, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, NULL, source);
+
+    psFree(version);
+    psFree(source);
+
+    return true;
 }
 
+void pswarpVersionPrint(void)
+{
+    psTime *time = psTimeGetNow(PS_TIME_TAI); // The time now
+    psString timeString = psTimeToISO(time); // The time in an ISO string
+    psFree(time);
+    psLogMsg("pswarp", PS_LOG_INFO, "pswarp at %s", timeString);
+    psFree(timeString);
+
+    psString pslib = psLibVersionLong();// psLib version
+    psString psmodules = psModulesVersionLong(); // psModules version
+    psString psphot = psphotVersionLong(); // psphot version
+    psString ppStats = ppStatsVersionLong(); // ppStats version
+    psString pswarp = pswarpVersionLong(); // pswarp version
+
+    psLogMsg("pswarp", PS_LOG_INFO, "%s", pslib);
+    psLogMsg("pswarp", PS_LOG_INFO, "%s", psmodules);
+    psLogMsg("pswarp", PS_LOG_INFO, "%s", psphot);
+    psLogMsg("pswarp", PS_LOG_INFO, "%s", ppStats);
+    psLogMsg("pswarp", PS_LOG_INFO, "%s", pswarp);
+
+    psFree(pslib);
+    psFree(psmodules);
+    psFree(psphot);
+    psFree(ppStats);
+    psFree(pswarp);
+
+    return;
+}
