Index: /branches/cnb_branches/cnb_branch_20090301/Nebulous-Server/Changes
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Nebulous-Server/Changes	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/Nebulous-Server/Changes	(revision 23594)
@@ -1,3 +1,6 @@
 Revision history for Nebulous
+
+0.17
+    - retry database transactions when a deadlock is detected
 
 0.16
Index: /branches/cnb_branches/cnb_branch_20090301/Nebulous-Server/bin/neb-admin
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Nebulous-Server/bin/neb-admin	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/Nebulous-Server/bin/neb-admin	(revision 23594)
@@ -152,4 +152,7 @@
         
     exit unless scalar @rows;
+    
+    # compare number of responses to limit below
+    my $Npending = @rows;
 
     print "replicatePending MULTI\n\n";
@@ -198,7 +201,10 @@
     }
 
-    return 1;
+    # use a different exit status if we hit the limit (likely more files pending)
+    if ($Npending == $limit) {
+	exit 1;
+    } 
+    exit 0;
 }
-
 
 sub removal
Index: /branches/cnb_branches/cnb_branch_20090301/Nebulous-Server/lib/Nebulous/Server.pm
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Nebulous-Server/lib/Nebulous/Server.pm	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/Nebulous-Server/lib/Nebulous/Server.pm	(revision 23594)
@@ -163,72 +163,79 @@
 
     my $uri;
-    eval {
-        {
-            # create storage_object
-            my $query = $db->prepare_cached( $sql->new_object ); 
-            $query->execute('NULL', $key->path);
-        }
-
-        my $so_id;
-        {
-            # get object ID
-            my $query = $db->prepare_cached( $sql->last_insert_id );
-            $query->execute;
-            ($so_id) = $query->fetchrow_array;
-            # XXX finish seems to be required when using LAST_INSERT_ID() or we
-            # get a warning about the stmt handling still be active the next
-            # time LAST_INSERT_ID() is invoked
-            $query->finish;
-        }
-
-        {
-            # create storage_object_attr
-            my $query = $db->prepare_cached( $sql->new_object_attr ); 
-            $query->execute($so_id);
-        }
-
-        {
-            
-            # create instance with no URI
+TRANS: while (1) {
+        eval {
+            {
+                # create storage_object
+                my $query = $db->prepare_cached( $sql->new_object ); 
+                $query->execute('NULL', $key->path);
+            }
+
+            my $so_id;
+            {
+                # get object ID
+                my $query = $db->prepare_cached( $sql->last_insert_id );
+                $query->execute;
+                ($so_id) = $query->fetchrow_array;
+                # XXX finish seems to be required when using LAST_INSERT_ID() or we
+                # get a warning about the stmt handling still be active the next
+                # time LAST_INSERT_ID() is invoked
+                $query->finish;
+            }
+
+            {
+                # create storage_object_attr
+                my $query = $db->prepare_cached( $sql->new_object_attr ); 
+                $query->execute($so_id);
+            }
+
+            {
+                
+                # create instance with no URI
 #            my $query = $db->prepare_cached( $sql->new_instance );
-            my $query = $db->prepare_cached( $sql->new_object_instance );
-            $query->execute($vol_id);
-        }
-
-        my $ins_id;
-        {
-            # get instance ID
-            my $query = $db->prepare_cached( $sql->last_insert_id );
-            $query->execute;
-            ($ins_id) = $query->fetchrow_array;
-            # XXX finish seems to be required when using LAST_INSERT_ID() or we
-            # get a warning about the stmt handling still be active the next
-            # time LAST_INSERT_ID() is invoked
-            $query->finish;
-        }
-
-        # Unfortunately, since we want to use the instance row's ID as part of the
-        # actual on disk file name we can't try to create the file until after
-        # we've create both a new storage_storage object and instance.
-
-        # TODO add some stuff here to retry if unsucessful
-        $uri = $self->_create_empty_instance_file($key->path, $so_id, $ins_id, $vol_path, $vol_xattr);
-        $log->debug("created $uri on volume ID: $vol_id");
-
-        {
-            # update the instance with URI & vol_id that the file is on
-            my $query = $db->prepare_cached( $sql->update_instance_uri );
-            # vol_id, uri, ins_id
-            $query->execute($vol_id, "$uri", $ins_id);
-        }
-
-        $db->commit;
-        $log->debug("commit");
-    };
-    if ($@) {
+                my $query = $db->prepare_cached( $sql->new_object_instance );
+                $query->execute($vol_id);
+            }
+
+            my $ins_id;
+            {
+                # get instance ID
+                my $query = $db->prepare_cached( $sql->last_insert_id );
+                $query->execute;
+                ($ins_id) = $query->fetchrow_array;
+                # XXX finish seems to be required when using LAST_INSERT_ID() or we
+                # get a warning about the stmt handling still be active the next
+                # time LAST_INSERT_ID() is invoked
+                $query->finish;
+            }
+
+            # Unfortunately, since we want to use the instance row's ID as part of the
+            # actual on disk file name we can't try to create the file until after
+            # we've create both a new storage_storage object and instance.
+
+            # TODO add some stuff here to retry if unsucessful
+            $uri = $self->_create_empty_instance_file($key->path, $so_id, $ins_id, $vol_path, $vol_xattr);
+            $log->debug("created $uri on volume ID: $vol_id");
+
+            {
+                # update the instance with URI & vol_id that the file is on
+                my $query = $db->prepare_cached( $sql->update_instance_uri );
+                # vol_id, uri, ins_id
+                $query->execute($vol_id, "$uri", $ins_id);
+            }
+
+            $db->commit;
+            $log->debug("commit");
+        };
+        if ($@) {
 #        and not $key->soft_volume
-        $db->rollback;
-        $log->debug("rollback");
-        $log->logdie("error: $@");
+            $db->rollback;
+            $log->debug("rollback");
+            if ($@ =~ /Deadlock found/) {
+                $log->warn("database deadlock retrying transaction: $@");
+                redo TRANS;
+            }
+            $log->logdie("error: $@");
+        }
+        last;
     }
 
@@ -269,24 +276,31 @@
     $newkey = parse_neb_key($newkey);
 
-    eval {
-        # rename storage_object
-        my $query = $db->prepare_cached($sql->rename_object); 
-        # this SQL statment takes the new key name as the first param
-        my $rows = $query->execute($newkey->path, $key->path);
-
-        # if we affected more then one row something very bad has happened.
-        unless ($rows == 1) {
-            $query->finish;
-            $log->logdie("affected row count is $rows instead of 1");
-        }
-
-        $db->commit;
-        $log->debug("commit");
-    };
-    if ($@) {
-        $db->rollback;
-        $log->debug("rollback");
-        $log->logdie("database error: $@");
-    }
+TRANS: while (1) {
+        eval {
+            # rename storage_object
+            my $query = $db->prepare_cached($sql->rename_object); 
+            # this SQL statment takes the new key name as the first param
+            my $rows = $query->execute($newkey->path, $key->path);
+
+            # if we affected more then one row something very bad has happened.
+            unless ($rows == 1) {
+                $query->finish;
+                $log->logdie("affected row count is $rows instead of 1");
+            }
+
+            $db->commit;
+            $log->debug("commit");
+        };
+        if ($@) {
+            $db->rollback;
+            $log->debug("rollback");
+            if ($@ =~ /Deadlock found/) {
+                $log->warn("database deadlock retrying transaction: $@");
+                redo TRANS;
+            }
+            $log->logdie("database error: $@");
+        }
+        last;
+    } 
 
     $log->debug("leaving");
@@ -330,51 +344,58 @@
     # key1.swap -> key2
 
-    eval {
-        {
-            # key1 -> key1.swap
-            my $query = $db->prepare_cached($sql->rename_object); 
-            # this SQL statment takes the new key name as the first param
-            my $rows = $query->execute($key1->path . ".swap", $key1->path);
-
-            # if we affected more then one row something very bad has happened.
-            unless ($rows == 1) {
-                $query->finish;
-                $log->logdie("affected row count is $rows instead of 1");
-            }
-        }
-
-        {
-            # key2 -> key1
-            my $query = $db->prepare_cached($sql->rename_object); 
-            # this SQL statment takes the new key name as the first param
-            my $rows = $query->execute($key1->path, $key2->path);
-
-            # if we affected more then one row something very bad has happened.
-            unless ($rows == 1) {
-                $query->finish;
-                $log->logdie("affected row count is $rows instead of 1");
-            }
-        }
-
-        {
-            # key1.swap -> key2
-            my $query = $db->prepare_cached($sql->rename_object); 
-            # this SQL statment takes the new key name as the first param
-            my $rows = $query->execute($key2->path, $key1->path . ".swap");
-
-            # if we affected more then one row something very bad has happened.
-            unless ($rows == 1) {
-                $query->finish;
-                $log->logdie("affected row count is $rows instead of 1");
-            }
-        }
-
-        $db->commit;
-        $log->debug("commit");
-    };
-    if ($@) {
-        $db->rollback;
-        $log->debug("rollback");
-        $log->logdie("database error: $@");
+TRANS: while (1) {
+        eval {
+            {
+                # key1 -> key1.swap
+                my $query = $db->prepare_cached($sql->rename_object); 
+                # this SQL statment takes the new key name as the first param
+                my $rows = $query->execute($key1->path . ".swap", $key1->path);
+
+                # if we affected more then one row something very bad has happened.
+                unless ($rows == 1) {
+                    $query->finish;
+                    $log->logdie("affected row count is $rows instead of 1");
+                }
+            }
+
+            {
+                # key2 -> key1
+                my $query = $db->prepare_cached($sql->rename_object); 
+                # this SQL statment takes the new key name as the first param
+                my $rows = $query->execute($key1->path, $key2->path);
+
+                # if we affected more then one row something very bad has happened.
+                unless ($rows == 1) {
+                    $query->finish;
+                    $log->logdie("affected row count is $rows instead of 1");
+                }
+            }
+
+            {
+                # key1.swap -> key2
+                my $query = $db->prepare_cached($sql->rename_object); 
+                # this SQL statment takes the new key name as the first param
+                my $rows = $query->execute($key2->path, $key1->path . ".swap");
+
+                # if we affected more then one row something very bad has happened.
+                unless ($rows == 1) {
+                    $query->finish;
+                    $log->logdie("affected row count is $rows instead of 1");
+                }
+            }
+
+            $db->commit;
+            $log->debug("commit");
+        };
+        if ($@) {
+            $db->rollback;
+            $log->debug("rollback");
+            if ($@ =~ /Deadlock found/) {
+                $log->warn("database deadlock retrying transaction: $@");
+                redo TRANS;
+            }
+            $log->logdie("database error: $@");
+        }
+        last;
     }
 
@@ -448,61 +469,68 @@
 
     my $uri;
-    eval {
-        my $so_id;
-        {
-            # verify that at least one instance is currently available
-            my $query = $db->prepare_cached( $sql->get_object_instances );
-            my $rows = $query->execute($key->path, 1);
-
-            unless ( $rows > 0 ) {
+TRANS: while (1) {
+        eval {
+            my $so_id;
+            {
+                # verify that at least one instance is currently available
+                my $query = $db->prepare_cached( $sql->get_object_instances );
+                my $rows = $query->execute($key->path, 1);
+
+                unless ( $rows > 0 ) {
+                    $query->finish;
+                    $log->logdie( "storage object does not exist" );
+                }
+
+                $so_id = $query->fetchrow_hashref->{ 'so_id' };
                 $query->finish;
-                $log->logdie( "storage object does not exist" );
-            }
-
-            $so_id = $query->fetchrow_hashref->{ 'so_id' };
-            $query->finish;
-        }
-
-        {
-            my $query = $db->prepare_cached( $sql->new_instance );
-            $query->execute($so_id, $vol_id);
-        }
-
-        my $ins_id;
-        {
-            my $query = $db->prepare_cached( $sql->last_insert_id );
-            $query->execute();
-            ($ins_id) = $query->fetchrow_array;
-            # XXX finish seems to be required when using LAST_INSERT_ID() or we
-            # get a warning about the stmt handling still being active the next
-            # time LAST_INSERT_ID() is invoked
-            $query->finish;
-        }
-
-        # Unfortunately, since we want to use the instance row's ID as part of
-        # the actual on disk file name we can't try to create the file until
-        # after we've create both a new storage_storage object and instance.
-
-        # TODO add some stuff here to retry if unsucessful
-        $uri = $self->_create_empty_instance_file($key->path, $so_id, $ins_id, $vol_path, $vol_xattr);
-
-        {
-            my $query = $db->prepare_cached( $sql->update_instance_uri );
-            # vol_id, uri, ins_id
-            $query->execute($vol_id, $uri, $ins_id);
-        }
-
-        $db->commit;
-        $log->debug("commit");
-    };
-    if ($@) {
-        $db->rollback;
-        # handle soft volumes
-        if (defined $vol_name and defined $key->soft_volume) {
-            $log->debug("retrying with 'any' volume");
-            return $self->replicate_object($key->path, 'any');
-        }
-        $log->debug("rollback");
-        $log->logdie("error: $@");
+            }
+
+            {
+                my $query = $db->prepare_cached( $sql->new_instance );
+                $query->execute($so_id, $vol_id);
+            }
+
+            my $ins_id;
+            {
+                my $query = $db->prepare_cached( $sql->last_insert_id );
+                $query->execute();
+                ($ins_id) = $query->fetchrow_array;
+                # XXX finish seems to be required when using LAST_INSERT_ID() or we
+                # get a warning about the stmt handling still being active the next
+                # time LAST_INSERT_ID() is invoked
+                $query->finish;
+            }
+
+            # Unfortunately, since we want to use the instance row's ID as part of
+            # the actual on disk file name we can't try to create the file until
+            # after we've create both a new storage_storage object and instance.
+
+            # TODO add some stuff here to retry if unsucessful
+            $uri = $self->_create_empty_instance_file($key->path, $so_id, $ins_id, $vol_path, $vol_xattr);
+
+            {
+                my $query = $db->prepare_cached( $sql->update_instance_uri );
+                # vol_id, uri, ins_id
+                $query->execute($vol_id, $uri, $ins_id);
+            }
+
+            $db->commit;
+            $log->debug("commit");
+        };
+        if ($@) {
+            $db->rollback;
+            # handle soft volumes
+            if (defined $vol_name and defined $key->soft_volume) {
+                $log->debug("retrying with 'any' volume");
+                return $self->replicate_object($key->path, 'any');
+            }
+            $log->debug("rollback");
+            if ($@ =~ /Deadlock found/) {
+                $log->warn("database deadlock retrying transaction: $@");
+                redo TRANS;
+            }
+            $log->logdie("error: $@");
+        }
+        last;
     }
 
@@ -545,67 +573,74 @@
     my $write_lock;
 
-    eval {
-        {
-            # this will set update locks
-            my $query = $db->prepare_cached( $sql->get_object_locks );
-            my $rows = $query->execute( $key->path );
-            unless ( $rows == 1 ) {
+TRANS: while (1) {
+        eval {
+            {
+                # this will set update locks
+                my $query = $db->prepare_cached( $sql->get_object_locks );
+                my $rows = $query->execute( $key->path );
+                unless ( $rows == 1 ) {
+                    $query->finish;
+                    $log->logdie( "storage object does not exist" );
+                }
+
+                my $row = $query->fetchrow_hashref;
                 $query->finish;
-                $log->logdie( "storage object does not exist" );
-            }
-
-            my $row = $query->fetchrow_hashref;
-            $query->finish;
-
-            $so_id      = $row->{ 'so_id' };
-            $read_lock  = $row->{ 'read_lock' };
-            $write_lock = $row->{ 'write_lock' };
-        }
-
-        if ($type eq 'write') {
-            # can't set a write lock twice and
-            # can't set a write lock if there are read locks
-            if ($write_lock) {
-                $log->logdie("can not write lock twice -- retry");
-            }
-            
-            if ($read_lock > 0) {
-                $log->logdie("can not write lock after read lock -- retry");
-            }
-
-            {
-                my $query = $db->prepare_cached( $sql->set_write_lock );
-                my $rows = $query->execute($key->path);
-            
-                # if we affected more then one row something very bad has happened.
-                unless ($rows == 1) {
-                    $log->logdie("affected row count is $rows instead of 1");
-                }
-
-            }
-        } elsif ($type eq 'read') {
-            # can't set a read lock if there's a write lock
-            if ($write_lock) {
-                $log->logdie("can not read lock after write lock -- retry");
-            }
-
-            {
-                my $query = $db->prepare_cached( $sql->increment_read_lock );
-                my $rows = $query->execute($key->path);
-            
-                # if we affected more then one row something very bad has happened.
-                unless ($rows == 1) {
-                    $log->logdie("affected row count is $rows instead of 1");
-                }
-            }
-        }
-
-        $db->commit;
-        $log->debug("commit");
-    };
-    if ($@) {
-        $db->rollback;
-        $log->debug("rollback");
-        $log->logdie("error: $@");
+
+                $so_id      = $row->{ 'so_id' };
+                $read_lock  = $row->{ 'read_lock' };
+                $write_lock = $row->{ 'write_lock' };
+            }
+
+            if ($type eq 'write') {
+                # can't set a write lock twice and
+                # can't set a write lock if there are read locks
+                if ($write_lock) {
+                    $log->logdie("can not write lock twice -- retry");
+                }
+                
+                if ($read_lock > 0) {
+                    $log->logdie("can not write lock after read lock -- retry");
+                }
+
+                {
+                    my $query = $db->prepare_cached( $sql->set_write_lock );
+                    my $rows = $query->execute($key->path);
+                
+                    # if we affected more then one row something very bad has happened.
+                    unless ($rows == 1) {
+                        $log->logdie("affected row count is $rows instead of 1");
+                    }
+
+                }
+            } elsif ($type eq 'read') {
+                # can't set a read lock if there's a write lock
+                if ($write_lock) {
+                    $log->logdie("can not read lock after write lock -- retry");
+                }
+
+                {
+                    my $query = $db->prepare_cached( $sql->increment_read_lock );
+                    my $rows = $query->execute($key->path);
+                
+                    # if we affected more then one row something very bad has happened.
+                    unless ($rows == 1) {
+                        $log->logdie("affected row count is $rows instead of 1");
+                    }
+                }
+            }
+
+            $db->commit;
+            $log->debug("commit");
+        };
+        if ($@) {
+            $db->rollback;
+            $log->debug("rollback");
+            if ($@ =~ /Deadlock found/) {
+                $log->warn("database deadlock retrying transaction: $@");
+                redo TRANS;
+            }
+            $log->logdie("error: $@");
+        }
+        last;
     }
 
@@ -648,70 +683,77 @@
     my $write_lock;
 
-    eval {
-        {
-            # this will set update locks
-            my $query = $db->prepare_cached( $sql->get_object_locks );
-            my $rows = $query->execute($key->path);
-            unless ($rows == 1) {
+TRANS: while (1) {
+        eval {
+            {
+                # this will set update locks
+                my $query = $db->prepare_cached( $sql->get_object_locks );
+                my $rows = $query->execute($key->path);
+                unless ($rows == 1) {
+                    $query->finish;
+                    $log->logdie("storage object does not exist");
+                }
+
+                my $row = $query->fetchrow_hashref;
                 $query->finish;
-                $log->logdie("storage object does not exist");
-            }
-
-            my $row = $query->fetchrow_hashref;
-            $query->finish;
-
-            $so_id      = $row->{ 'so_id' };
-            $read_lock  = $row->{ 'read_lock' };
-            $write_lock = $row->{ 'write_lock' };
-        }
-
-        if ($type eq 'write') {
-            # can't remove a write lock if it doesn't exist
-            if ($read_lock) {
-                $log->logdie("can not have a write lock under a read lock");
-            }
-
-            unless ($write_lock) {
-                $log->logdie("can not remove non-existant write lock");
-            }
-
-            {
-                my $query = $db->prepare_cached( $sql->delete_write_lock );
-                my $rows = $query->execute($key->path);
-            
-                # if we affected more then one row something very bad has happened.
-                unless ($rows == 1) {
-                    $log->logdie("affected row count is $rows instead of 1");
-                }
-            }
-        } elsif ($type eq 'read') {
-            # can't remove a read lock if there's a write lock and
-            # can't remove a read lock if there aren't any
-            if ($write_lock) {
-                $log->logdie("can not have a read lock under a write lock");
-            }
-               
-            if ($read_lock == 0) {
-                $log->logdie("can not remove non-existant read lock");
-            }
-
-            {
-                my $query = $db->prepare_cached( $sql->decrement_read_lock );
-                my $rows = $query->execute($key->path);
-            
-                # if we affected more then one row something very bad has happened.
-                unless ($rows == 1) {
-                    $log->logdie("affected row count is $rows instead of 1");
-                }
-
-            }
-        }
-        $db->commit;
-        $log->debug("commit");
-    };
-    if ($@) {
-        $db->rollback;
-        $log->debug("rollback");
-        $log->logdie("error: $@");
+
+                $so_id      = $row->{ 'so_id' };
+                $read_lock  = $row->{ 'read_lock' };
+                $write_lock = $row->{ 'write_lock' };
+            }
+
+            if ($type eq 'write') {
+                # can't remove a write lock if it doesn't exist
+                if ($read_lock) {
+                    $log->logdie("can not have a write lock under a read lock");
+                }
+
+                unless ($write_lock) {
+                    $log->logdie("can not remove non-existant write lock");
+                }
+
+                {
+                    my $query = $db->prepare_cached( $sql->delete_write_lock );
+                    my $rows = $query->execute($key->path);
+                
+                    # if we affected more then one row something very bad has happened.
+                    unless ($rows == 1) {
+                        $log->logdie("affected row count is $rows instead of 1");
+                    }
+                }
+            } elsif ($type eq 'read') {
+                # can't remove a read lock if there's a write lock and
+                # can't remove a read lock if there aren't any
+                if ($write_lock) {
+                    $log->logdie("can not have a read lock under a write lock");
+                }
+                   
+                if ($read_lock == 0) {
+                    $log->logdie("can not remove non-existant read lock");
+                }
+
+                {
+                    my $query = $db->prepare_cached( $sql->decrement_read_lock );
+                    my $rows = $query->execute($key->path);
+                
+                    # if we affected more then one row something very bad has happened.
+                    unless ($rows == 1) {
+                        $log->logdie("affected row count is $rows instead of 1");
+                    }
+
+                }
+            }
+            $db->commit;
+            $log->debug("commit");
+        };
+        if ($@) {
+            $db->rollback;
+            $log->debug("rollback");
+            if ($@ =~ /Deadlock found/) {
+                $log->warn("database deadlock retrying transaction: $@");
+                redo TRANS;
+            }
+            $log->logdie("error: $@");
+        }
+        last;
     }
 
@@ -756,38 +798,45 @@
     $key = parse_neb_key($key);
 
-    eval {
-        my $query;
-
-        if ($flags eq 'create') {
-            $query = $db->prepare_cached( $sql->new_object_xattr );
-        } else {
-            # replace
-            $query = $db->prepare_cached( $sql->replace_object_xattr );
-        }
-
-        # name, value, ext_id
-        my $rows = $query->execute($name, $value, $key->path);
-        $query->finish;
-
-        # if we affected more then one row something very bad has happened.
-        if ($flags eq 'create') {
-            unless ($rows == 1) {
-                $log->logdie( "affected row count is $rows instead of 1" );
-            }
-        } else {
-            # replace_object_xattr can effect either 1 or 2 rows.  2 rows in
-            # the case of a replace and 1 if the xattr didn't already exist.
-            unless ($rows == 1 or $rows == 2) {
-                $log->logdie( "affected row count is $rows instead of 2" );
-            }
-        }
-
-        $db->commit;
-        $log->debug("commit");
-    };
-    if ($@) {
-        $db->rollback;
-        $log->debug("rollback");
-        $log->logdie("database error: $@");
+TRANS: while (1) {
+        eval {
+            my $query;
+
+            if ($flags eq 'create') {
+                $query = $db->prepare_cached( $sql->new_object_xattr );
+            } else {
+                # replace
+                $query = $db->prepare_cached( $sql->replace_object_xattr );
+            }
+
+            # name, value, ext_id
+            my $rows = $query->execute($name, $value, $key->path);
+            $query->finish;
+
+            # if we affected more then one row something very bad has happened.
+            if ($flags eq 'create') {
+                unless ($rows == 1) {
+                    $log->logdie( "affected row count is $rows instead of 1" );
+                }
+            } else {
+                # replace_object_xattr can effect either 1 or 2 rows.  2 rows in
+                # the case of a replace and 1 if the xattr didn't already exist.
+                unless ($rows == 1 or $rows == 2) {
+                    $log->logdie( "affected row count is $rows instead of 2" );
+                }
+            }
+
+            $db->commit;
+            $log->debug("commit");
+        };
+        if ($@) {
+            $db->rollback;
+            $log->debug("rollback");
+            if ($@ =~ /Deadlock found/) {
+                $log->warn("database deadlock retrying transaction: $@");
+                redo TRANS;
+            }
+            $log->logdie("database error: $@");
+        }
+        last;
     }
 
@@ -919,23 +968,30 @@
     $key = parse_neb_key($key);
 
-    eval {
-        my $query = $db->prepare_cached( $sql->remove_object_xattr );
-        # ext_id, name
-        my $rows = $query->execute($key->path, $name);
-        $query->finish;
-
-        # if we affected more then one row something very bad has happened.
-        unless ($rows == 1) {
-            $log->logdie( "affected row count is $rows instead of 1" );
-        }
-
-        $db->commit;
-        $log->debug("commit");
-    };
-    if ($@) {
-        $db->rollback;
-        $log->debug("rollback");
-        $log->logdie("database error: $@");
-    }
+TRANS: while (1) {
+        eval {
+            my $query = $db->prepare_cached( $sql->remove_object_xattr );
+            # ext_id, name
+            my $rows = $query->execute($key->path, $name);
+            $query->finish;
+
+            # if we affected more then one row something very bad has happened.
+            unless ($rows == 1) {
+                $log->logdie( "affected row count is $rows instead of 1" );
+            }
+
+            $db->commit;
+            $log->debug("commit");
+        };
+        if ($@) {
+            $db->rollback;
+            $log->debug("rollback");
+            if ($@ =~ /Deadlock found/) {
+                $log->warn("database deadlock retrying transaction: $@");
+                redo TRANS;
+            }
+            $log->logdie("database error: $@");
+        }
+        last;
+    } 
 
     $log->debug("leaving");
@@ -1099,64 +1155,71 @@
     $log->debug( "entered - @_" );
 
-    eval {
-        my $so_id;
-        my $instances;
-        # get so_id
-        {
-            my $query = $db->prepare_cached( $sql->get_object_from_uri );
-            my $rows = $query->execute( $uri );
-
-            unless ( $rows > 0 ) {
+TRANS: while (1) {
+        eval {
+            my $so_id;
+            my $instances;
+            # get so_id
+            {
+                my $query = $db->prepare_cached( $sql->get_object_from_uri );
+                my $rows = $query->execute( $uri );
+
+                unless ( $rows > 0 ) {
+                    $query->finish;
+                    $log->logdie( "no instance is associated with uri" );
+                }
+
+                $so_id = $query->fetchrow_hashref->{ 'so_id' };
                 $query->finish;
-                $log->logdie( "no instance is associated with uri" );
-            }
-
-            $so_id = $query->fetchrow_hashref->{ 'so_id' };
-            $query->finish;
-
-        }
-
-        {
-            my $query = $db->prepare_cached( $sql->get_instance_count );
-            $query->execute( $so_id );
-
-            $instances = $query->fetchrow_hashref->{ 'count(ins_id)' };
-            $query->finish;
-        }
-
-        # remove instance
-        {
-            my $query = $db->prepare_cached( $sql->delete_instance );
-            my $rows = $query->execute( $uri );
-            $query->finish;
-            
-            # if we affected something other then two rows something very bad
-            # has happened
-            unless ( $rows == 1 ) {
-                $log->logdie( "affected row count is $rows instead of 1" );
-            }
-        }
-
-        # if we just deleted the last instance associated with a storage object
-        # remove it too
-        if ( $instances == 1 ) {
-            # we just removed the last instance
-            my $query = $db->prepare_cached( $sql->delete_object );
-            my $rows = $query->execute( $so_id );
-            $query->finish;
-
-            # TODO: this will have to be changed in order to support hardlinks
-            unless ( $rows == 1 ) {
-                $log->logdie( "affected row count is $rows instead of 2" );
-            }
-        }
-
-        $db->commit;
-        $log->debug("commit");
-    };
-    if ( $@ ) {
-        $db->rollback;
-        $log->debug("rollback");
-        $log->logdie( "database error: $@" );
+
+            }
+
+            {
+                my $query = $db->prepare_cached( $sql->get_instance_count );
+                $query->execute( $so_id );
+
+                $instances = $query->fetchrow_hashref->{ 'count(ins_id)' };
+                $query->finish;
+            }
+
+            # remove instance
+            {
+                my $query = $db->prepare_cached( $sql->delete_instance );
+                my $rows = $query->execute( $uri );
+                $query->finish;
+                
+                # if we affected something other then two rows something very bad
+                # has happened
+                unless ( $rows == 1 ) {
+                    $log->logdie( "affected row count is $rows instead of 1" );
+                }
+            }
+
+            # if we just deleted the last instance associated with a storage object
+            # remove it too
+            if ( $instances == 1 ) {
+                # we just removed the last instance
+                my $query = $db->prepare_cached( $sql->delete_object );
+                my $rows = $query->execute( $so_id );
+                $query->finish;
+
+                # TODO: this will have to be changed in order to support hardlinks
+                unless ( $rows == 1 ) {
+                    $log->logdie( "affected row count is $rows instead of 2" );
+                }
+            }
+
+            $db->commit;
+            $log->debug("commit");
+        };
+        if ( $@ ) {
+            $db->rollback;
+            $log->debug("rollback");
+            if ($@ =~ /Deadlock found/) {
+                $log->warn("database deadlock retrying transaction: $@");
+                redo TRANS;
+            }
+            $log->logdie( "database error: $@" );
+        }
+        last;
     }
 
Index: /branches/cnb_branches/cnb_branch_20090301/Nebulous/scripts/stats.pl
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Nebulous/scripts/stats.pl	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/Nebulous/scripts/stats.pl	(revision 23594)
@@ -33,5 +33,5 @@
         my $stat = Statistics::Descriptive::Sparse->new();
         $stat->add_data(@$duration);
-        printf("%-40s %f\n", "$method: mean duration: ", $stat->mean());
+        printf("%-40s : %f\n", "$method - mean duration", $stat->mean());
     }
     {
@@ -40,11 +40,12 @@
         $stat->add_data(@$times);
         $total_stat->add_data(@$times);
-        printf("%-40s %f\n","$method: calls/s: ", $stat->count / ($stat->max - $stat->min));
+        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";
+print "\n";
+printf("%-40s : %f\n", "first timestamp", $total_stat->min);
+printf("%-40s : %f\n", "last timestamp", $total_stat->max);
+printf("%-40s : %f\n", "total wallclock seconds", ($total_stat->max - $total_stat->min));
+printf("%-40s : %f\n", "total call count", $total_stat->count);
+printf("%-40s : %f\n", "total calls/s:", $total_stat->count / ($total_stat->max - $total_stat->min));
Index: /branches/cnb_branches/cnb_branch_20090301/Ohana/src/libohana/src/IOBufferOps.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Ohana/src/libohana/src/IOBufferOps.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/Ohana/src/libohana/src/IOBufferOps.c	(revision 23594)
@@ -27,5 +27,5 @@
 int ReadtoIOBuffer (IOBuffer *buffer, int fd) {
 
-  int Nread, Nfree;
+  int Nread, Nfree, Nwant;
 
   if (fd == 0) {
@@ -34,4 +34,5 @@
   }
 
+  // if we run out of space, double Nblock
   Nfree = buffer[0].Nalloc - buffer[0].Nbuffer;
   if (Nfree < buffer[0].Nblock) {
@@ -44,6 +45,8 @@
   }
 
-  Nread = read (fd, &buffer[0].buffer[buffer[0].Nbuffer], buffer[0].Nblock);
-  if (DEBUG) fprintf (stderr, "read IO buffer: (%lx) %d from %d\n", (unsigned long) buffer, Nread, buffer[0].Nblock); 
+  // ensure we never read more than space available
+  Nwant = MIN (Nfree, buffer[0].Nblock);
+  Nread = read (fd, &buffer[0].buffer[buffer[0].Nbuffer], Nwant);
+  if (DEBUG) fprintf (stderr, "read IO buffer: (%lx) %d from %d\n", (unsigned long) buffer, Nread, Nwant); 
 
   /* on success, increase the block size for the next read */
Index: /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/cmd.basic/break.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/cmd.basic/break.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/cmd.basic/break.c	(revision 23594)
@@ -8,6 +8,6 @@
     remove_argument (N, &argc, argv);
     value = -1;
-    if (!strcasecmp (argv[N], "on")) value = TRUE;
-    if (!strcasecmp (argv[N], "off")) value = FALSE;
+    if (!strcasecmp (argv[N], "on")) value = 1;
+    if (!strcasecmp (argv[N], "off")) value = 0;
     if (value == -1) {
       gprint (GP_ERR, "USAGE: break -auto [on / off]\n");
Index: /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/cmd.basic/run_if.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/cmd.basic/run_if.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/cmd.basic/run_if.c	(revision 23594)
@@ -113,5 +113,8 @@
 	status = multicommand (input);
 	if (ThisList == 0) add_history (input);
-	if (auto_break && !status) return (FALSE);
+	if (auto_break && !status) {
+	  free (input);
+	  return (FALSE);
+	}
       }
     } 
Index: /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/include/pantasks.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/include/pantasks.h	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/include/pantasks.h	(revision 23594)
@@ -244,4 +244,5 @@
 int CheckControllerOutput (void);
 int PrintControllerOutput (void);
+void PrintControllerBusyJobs ();
 
 int AddHost (char *hostname, int max_threads);
@@ -260,11 +261,14 @@
 
 // functions related to the server threads
+void *CheckJobsAndTasksThread (void *data);
+
 void CheckTasksSetState (int state);
 int CheckTasksGetState (void);
-void *CheckTasksThread (void *data);
 
 void CheckJobsSetState (int state);
 int CheckJobsGetState (void);
-void *CheckJobsThread (void *data);
+
+// void *CheckTasksThread (void *data);
+// void *CheckJobsThread (void *data);
 
 void CheckControllerSetState (int state);
@@ -283,6 +287,12 @@
 void CheckInputs (void);
 
-void SerialThreadLock (void);
-void SerialThreadUnlock (void);
+void ClientThreadLock (void);
+void ClientThreadUnlock (void);
+void CommandThreadLock (void);
+void CommandThreadUnlock (void);
+void ControlThreadLock (void);
+void ControlThreadUnlock (void);
+void JobTaskThreadLock (void);
+void JobTaskThreadUnlock (void);
 
 int InitPassword (void);
Index: /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/include/shell.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/include/shell.h	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/include/shell.h	(revision 23594)
@@ -166,4 +166,6 @@
 int           gprint          		PROTO((gpDest dest, char *format, ...));
 int           gwrite          		PROTO((char *buffer, int size, int N, gpDest dest));
+int           gprint_syserror           PROTO((gpDest dest, int myError, char *format, ...));
+int           gprintv                   PROTO((gpDest dest, char *format, va_list argp));
 
 /* socket functions */
Index: /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/lib.shell/SocketOps.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/lib.shell/SocketOps.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/lib.shell/SocketOps.c	(revision 23594)
@@ -5,4 +5,6 @@
 # define DEBUG 0
 
+// these three static variables are only modified in the setup command before
+// the threads are started.  Thread-safety is not a problem for these.
 static int NVALID;
 static int Nvalid;
Index: /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/lib.shell/gprint.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/lib.shell/gprint.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/lib.shell/gprint.c	(revision 23594)
@@ -287,11 +287,19 @@
 
   int status;
-  gpStream *stream;
   va_list argp;  
 
+  va_start (argp, format);
+  status = gprintv (dest, format, argp);
+  va_end (argp);
+  return (status);
+}
+
+int gprintv (gpDest dest, char *format, va_list argp) {
+
+  int status;
+  gpStream *stream;
+
   // this thread only writes to its own stream
   stream = gprintGetStream (dest);
-
-  va_start (argp, format);
 
   if (stream[0].mode == GP_FILE) {
@@ -303,5 +311,4 @@
     vPrintIOBuffer (stream[0].buffer, format, argp);
   }
-  va_end (argp);
   return (TRUE);
 }
@@ -332,11 +339,26 @@
 }
 
-/* I'm going to need to have different output targets for different threads
-   we can do this with these functions:
-
-   pthread_t pthread_self(void);
-   int pthread_equal(pthread_t thread1, pthread_t thread2);
-   // returns TRUE if equal, FALSE if not
-   
-*/
-
+# define MAX_ERROR_LENGTH 256           // Maximum length string for error messages
+
+// print an error (based on errno values) to gprint destination
+int gprint_syserror (gpDest dest, int myError, char *format, ...) {
+
+  char errorBuf[MAX_ERROR_LENGTH];
+  char *errorMsg;
+  va_list argp;  
+
+  // there are two strerror_r implementations; choose the right one:
+#if ((_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600) && ! _GNU_SOURCE)
+  errorMsg = strerror_r (myError, errorBuf, MAX_ERROR_LENGTH);
+#else
+  strerror_r (myError, errorBuf, MAX_ERROR_LENGTH);
+  errorMsg = errorBuf;
+#endif
+
+  va_start (argp, format);
+  gprintv (dest, format, argp);
+  va_end (argp);
+
+  gprintv (dest, "%s\n", errorMsg);
+  return TRUE;
+}
Index: /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/CheckController.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/CheckController.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/CheckController.c	(revision 23594)
@@ -1,7 +1,6 @@
 # include "pantasks.h"
 
-static struct timeval start;
-void TimerMark ();
-float TimerElapsed (int reset);
+void TimerMark (struct timeval *start);
+float TimerElapsed (struct timeval *start, int reset);
 
 int CheckController () {
@@ -11,4 +10,5 @@
   Job *job;
   IOBuffer buffer;
+  struct timeval start;
 
   /* get the list of completed jobs (exit / crash), update the job status */
@@ -17,12 +17,10 @@
   /*** check EXIT jobs ***/
   InitIOBuffer (&buffer, 0x100);
-  // TimerMark ();
-  // status = ControllerCommand ("stop", CONTROLLER_PROMPT, &buffer); 
-  // if (VerboseMode()) gprint (GP_ERR, "stop controller %f\n", TimerElapsed(TRUE));
 
-  TimerMark ();
+  TimerMark (&start);
   FlushIOBuffer (&buffer);
+
   status = ControllerCommand ("jobstack exit", CONTROLLER_PROMPT, &buffer);
-  if (VerboseMode()) gprint (GP_ERR, "check exit stack %f\n", TimerElapsed(TRUE));
+  if (VerboseMode()) gprint (GP_ERR, "check exit stack %f\n", TimerElapsed(&start, TRUE));
   if (!status) goto escape;
 
@@ -34,5 +32,5 @@
   status = sscanf (buffer.buffer, "%*s %d", &Njobs);
   if (status != 1) goto escape;
-  if (VerboseMode()) gprint (GP_ERR, "parse %d jobs on stack %f\n", Njobs, TimerElapsed(TRUE));
+  if (VerboseMode()) gprint (GP_ERR, "parse %d jobs on stack %f\n", Njobs, TimerElapsed(&start, TRUE));
 
   p = buffer.buffer;
@@ -46,13 +44,18 @@
     status = sscanf (p, "%d", &JobID);
 
+    // the operations within this locked block only interact with the controller or
+    // modify the properties of the selected job
+    JobTaskLock();
     job = FindControllerJob (JobID);
     if (job == NULL) {
       gprint (GP_ERR, "misplaced job? %d not in EXIT job list\n", JobID);
+      JobTaskUnlock();
       continue;
     }
     /* this checks the individual job status, grabs stdout/stderr */
     CheckControllerJob (job);
+    JobTaskUnlock();
   }
-  if (VerboseMode()) gprint (GP_ERR, "clear %d exit jobs %f\n", i, TimerElapsed(TRUE));
+  if (VerboseMode()) gprint (GP_ERR, "clear %d exit jobs %f\n", i, TimerElapsed(&start, TRUE));
 
   /*** check CRASH jobs ***/
@@ -67,5 +70,5 @@
   status = sscanf (buffer.buffer, "%*s %d", &Njobs);
   if (status != 1) goto escape;
-  if (VerboseMode()) gprint (GP_ERR, "check crash stack %f\n", TimerElapsed(TRUE)); 
+  if (VerboseMode()) gprint (GP_ERR, "check crash stack %f\n", TimerElapsed(&start, TRUE)); 
 
   p = buffer.buffer;
@@ -77,18 +80,22 @@
     }
     p = q + 1;
-    
     status = sscanf (p, "%d", &JobID);
+
+    // the operations within this locked block only interact with the controller or
+    // modify the properties of the selected job
+    JobTaskLock();
     job = FindControllerJob (JobID);
     if (job == NULL) {
       gprint (GP_ERR, "misplaced job? %d not in CRASH job list\n", JobID);
+      JobTaskUnlock();
       continue;
     }
     /* this checks the individual job status, grabs stdout/stderr */
     CheckControllerJob (job);
+    JobTaskUnlock();
   }
-  if (VerboseMode()) gprint (GP_ERR, "clear %d crash jobs %f\n", i, TimerElapsed(TRUE)); 
+  if (VerboseMode()) gprint (GP_ERR, "clear %d crash jobs %f\n", i, TimerElapsed(&start, TRUE)); 
 
   FlushIOBuffer (&buffer);
-  // status = ControllerCommand ("run", CONTROLLER_PROMPT, &buffer);
   FreeIOBuffer (&buffer);
   return (TRUE);
@@ -96,14 +103,13 @@
  escape:
   FlushIOBuffer (&buffer);
-  // status = ControllerCommand ("run", CONTROLLER_PROMPT, &buffer); 
   FreeIOBuffer (&buffer);
   return (FALSE);
 }
 
-void TimerMark () {
-    gettimeofday (&start, (void *) NULL);
+void TimerMark (struct timeval *start) {
+    gettimeofday (start, (void *) NULL);
 }
 
-float TimerElapsed (int reset) {
+float TimerElapsed (struct timeval *start, int reset) {
 
   float dtime;
@@ -111,6 +117,6 @@
 
   gettimeofday (&stop, (void *) NULL);
-  dtime = DTIME (stop, start);
-  if (reset) gettimeofday (&start, (void *) NULL);
+  dtime = DTIME (stop, start[0]);
+  if (reset) gettimeofday (start, (void *) NULL);
   return (dtime);
 }
Index: /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/CheckJobs.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/CheckJobs.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/CheckJobs.c	(revision 23594)
@@ -18,4 +18,5 @@
   next_timeout = 1.0;
 
+  JobTaskLock();
   /** test all jobs: ready to test?  finished? **/
   while ((job = NextJob ()) != NULL) {
@@ -77,6 +78,6 @@
 	/* set taskarg variables */
 	for (i = 0; i < job[0].argc; i++) {
-	    sprintf (varname, "taskarg:%d", i);
-	    set_str_variable (varname, job[0].argv[i]);
+	  sprintf (varname, "taskarg:%d", i);
+	  set_str_variable (varname, job[0].argv[i]);
 	}
 	set_int_variable ("taskarg:n", job[0].argc);
@@ -84,6 +85,6 @@
 	/* set options variables */
 	for (i = 0; i < job[0].optc; i++) {
-	    sprintf (varname, "options:%d", i);
-	    set_str_variable (varname, job[0].optv[i]);
+	  sprintf (varname, "options:%d", i);
+	  set_str_variable (varname, job[0].optv[i]);
 	}
 	set_int_variable ("options:n", job[0].optc);
@@ -109,5 +110,10 @@
 	  if (VerboseMode()) gprint (GP_LOG, "job %s (%d) crash\n", task[0].name, job[0].JobID);
 	  if (task[0].crash != NULL) {
+	    // we need to unlock JobTask since JobTaskLock is called inside CommandLock in the client thread
+	    JobTaskUnlock();
+	    CommandLock();
 	    exec_loop (task[0].crash);
+	    CommandUnlock();
+	    JobTaskLock();
 	  }
 	}
@@ -133,5 +139,12 @@
 	    }
 	  }
-	  if (macro != NULL) exec_loop (macro);
+	  if (macro != NULL) {
+	    // we need to unlock JobTask since JobTaskLock is called inside CommandLock in the client thread
+	    JobTaskUnlock();
+	    CommandLock();
+	    exec_loop (macro);
+	    CommandUnlock();
+	    JobTaskLock();
+	  }
 	}
 
@@ -146,5 +159,4 @@
 	DeleteJob (job);
 	continue;
-	break;
 
       default:
@@ -156,5 +168,5 @@
     /* check for timeout - (local jobs only) 
        we only check timeout after a poll (forces at least one poll)
-     */
+    */
     if (job[0].mode == JOB_LOCAL) {
       if (GetTaskTimer(job[0].start, FALSE) < task[0].timeout_period) { 
@@ -179,6 +191,6 @@
       /* set taskarg variables */
       for (i = 0; i < job[0].argc; i++) {
-	  sprintf (varname, "taskarg:%d", i);
-	  set_str_variable (varname, job[0].argv[i]);
+	sprintf (varname, "taskarg:%d", i);
+	set_str_variable (varname, job[0].argv[i]);
       }
       set_int_variable ("taskarg:n", job[0].argc);
@@ -193,5 +205,10 @@
       /* run task[0].timeout macro, if it exists */
       if (task[0].timeout != NULL) {
+	// we need to unlock JobTask since JobTaskLock is called inside CommandLock in the client thread
+	JobTaskUnlock();
+	CommandLock();
 	exec_loop (task[0].timeout);
+	CommandUnlock();
+	JobTaskLock();
       }
 
@@ -205,4 +222,5 @@
   }
   // fprintf (stderr, "check %d jobs\n", Ncheck);
+  JobTaskUnlock();
   return (next_timeout);
 }
@@ -210,21 +228,21 @@
 /* 
 
-  job / task timeline:
-
-  task:
-  0           exec     
-  start       create
-  task clock  new job
-
-  job:
-  0           1xpoll     2xpoll     3xpoll
-  start       check      check      check 
-  job clock   status     status     status
-
-  .           .          .          timeout
-                                    run
-				    timeout
-
-  must be at least one poll before timeout 
-  (timeout >= poll)
+   job / task timeline:
+
+   task:
+   0           exec     
+   start       create
+   task clock  new job
+
+   job:
+   0           1xpoll     2xpoll     3xpoll
+   start       check      check      check 
+   job clock   status     status     status
+
+   .           .          .          timeout
+   run
+   timeout
+
+   must be at least one poll before timeout 
+   (timeout >= poll)
 */
Index: /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/CheckPassword.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/CheckPassword.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/CheckPassword.c	(revision 23594)
@@ -2,4 +2,6 @@
 # define DEBUG 0
 
+// this static var is only used by InitPassword and CheckPassword below.
+// Both functions are only called by the main thread.
 static char PASSWORD[256];
 
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 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/CheckTasks.c	(revision 23594)
@@ -7,9 +7,9 @@
   int status;
   float time_running, next_timeout, fuzz;
-  // struct timeval now;
 
   // actual maximum delay is controlled in job_threads.c
   next_timeout = 1.0;
 
+  JobTaskLock();
   /** test all tasks: ready to test? ready to run? **/
   while ((task = NextTask ()) != NULL) {
@@ -48,5 +48,5 @@
     // add random offset between 0 and 5% of exec_period
     // XXX this should be optional
-    fuzz = 0.1*task[0].exec_period*drand48();
+    fuzz = task[0].exec_period*(0.5*drand48() - 0.25);
     task[0].last.tv_usec = 1e6*(fuzz - (int)fuzz);
     task[0].last.tv_sec += (int) fuzz;
@@ -54,12 +54,14 @@
     /* ready to run? : run task.exec macro */
     if (task[0].exec != NULL) {
+      // we need to unlock JobTask since JobTaskLock is called inside CommandLock in the client thread
+      JobTaskUnlock();
+      CommandLock();
       status = exec_loop (task[0].exec);
+      CommandUnlock();
+      JobTaskLock();
       if (!status) {
 	continue;
       }
     }
-
-    // gettimeofday (&now, (void *) NULL);
-    // fprintf (stderr, "t1: %d %6d  - \n", now.tv_sec, now.tv_usec);
 
     /* check if there are errors with this task */
@@ -68,30 +70,23 @@
     }
     
-    // gettimeofday (&now, (void *) NULL);
-    // fprintf (stderr, "t2: %d %6d  - \n", now.tv_sec, now.tv_usec);
-
     /* construct job from task */
     job = CreateJob (task);
+    if (!job) {
+      continue;
+    }
+
     if (DEBUG) fprintf (stderr, "create job: (%zx) %d of %d\n", (size_t) job[0].stdout_buff.buffer, job[0].stdout_buff.Nbuffer, job[0].stdout_buff.Nalloc); 
 
-    // gettimeofday (&now, (void *) NULL);
-    // fprintf (stderr, "t3: %d %6d  - \n", now.tv_sec, now.tv_usec);
-
-    /* execute job - XXX add status test */
-    SubmitJob (job);
-
-    // fprintf (stderr, "nl: %d %6d  - ",
-    // task[0].last.tv_sec, task[0].last.tv_usec);
-
-    /* increment job counters */
-    task[0].Njobs ++;
-    task[0].Npending ++;
-
-    // fprintf (stderr, "%d %6d\n", 
-    // task[0].last.tv_sec, task[0].last.tv_usec);
+    /* execute job */
+    if (!SubmitJob (job)) {
+      DeleteJob (job);
+      continue;
+    }
+    task[0].Njobs ++; // number of jobs successfully submitted
 
     /* increment Nrun for inclusive ranges with Nmax */
     BumpTimeRanges (task[0].ranges, task[0].Nranges);
   }
+  JobTaskUnlock();
   return (next_timeout);
 }
Index: /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/ControllerOps.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/ControllerOps.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/ControllerOps.c	(revision 23594)
@@ -12,4 +12,6 @@
 
 /* local static variables to track the controller host properties */
+/* these are used by AddHost and DeleteHost, and are only called by controller_host.c (clientThread) */
+/* or by RestartController : lock between these two? */
 static Host *hosts = NULL;
 static int Nhosts = 0;
@@ -31,4 +33,7 @@
 }
 
+// XXX possible race condition problem: if we delete a host while the controller
+// is being restarted.  to fix this, we need to keep the deletion in controller_thread,
+// but perhaps mark it in the client_thread?
 int DeleteHost (char *hostname) {
 
@@ -261,7 +266,14 @@
   }
 
+  // This function is called by the JobTaskThread via SubmitJob.  We need to unlock the
+  // JobTaskLock to avoid a dead lock with the JobTaskLock called in CheckController
+  JobTaskUnlock();
+  ControlLock(__func__);
   InitIOBuffer (&buffer, 0x100);
   status = ControllerCommand (cmd, CONTROLLER_PROMPT, &buffer);
   free (cmd);
+  ControlUnlock(__func__);
+  JobTaskLock();
+
 
   /* extract the job PID from the controller response */
@@ -274,6 +286,10 @@
   }
   sscanf (p, "%*s %s", string);
+  FreeIOBuffer (&buffer);
+
   job[0].pid = atoi (string);
-  FreeIOBuffer (&buffer);
+  if (job[0].pid < 0) {
+    return (FALSE);
+  }
   return (TRUE);
 }
@@ -411,5 +427,5 @@
   if ((status == -1) && (errno == EPIPE)) {
     StopController ();
-    gprint (GP_ERR, "controller is down (pipe closed), restarting\n");
+    fprintf (stderr, "controller is down (pipe closed), restarting\n");
     if (!RestartController ()) {
       return (FALSE);
@@ -434,5 +450,5 @@
     if (status ==  0) {
       StopController ();
-      gprint (GP_ERR, "controller is down (EOF), restarting\n");
+      fprintf (stderr, "controller is down (EOF), restarting\n");
       if (!RestartController ()) {
 	return (FALSE);
@@ -441,10 +457,10 @@
     }
     if (status == -1) {
-      gprint (GP_ERR, "controller is not responding (%d tries)\n", j);
+      fprintf (stderr, "controller is not responding (%d tries)\n", j);
       gwrite (buffer[0].buffer, 1, buffer[0].Nbuffer, GP_ERR);
     }
   }
   if (status == -1) {
-    gprint (GP_ERR, "controller still not responding, giving up\n");
+    fprintf (stderr, "controller still not responding, giving up\n");
     return (FALSE);
   }
@@ -456,5 +472,5 @@
     bzero (buffer[0].buffer + buffer[0].Nbuffer, buffer[0].Nalloc - buffer[0].Nbuffer);
   }
-  /* if (VerboseMode()) gprint (GP_ERR, "message received, %d cycles\n", i); */
+  if (VerboseMode()) fprintf (stderr, "message received, %d cycles\n", i);
   return (TRUE);
 }
@@ -492,4 +508,31 @@
 }
 
+void PrintControllerBusyJobs () {
+
+  int status;
+  char command[1024];
+  IOBuffer buffer;
+
+  /* check if controller is running */
+  status = CheckControllerStatus ();
+  if (!status) {
+    return;
+  }
+
+  sprintf (command, "jobstack busy");
+  InitIOBuffer (&buffer, 0x100);
+
+  status = ControllerCommand (command, CONTROLLER_PROMPT, &buffer);
+
+  if (status) {
+    gprint (GP_LOG, " jobs currently running remotely:\n");
+    gwrite (buffer.buffer, 1, buffer.Nbuffer, GP_LOG);
+  } else {
+    gprint (GP_LOG, "controller is not responding\n");
+  }
+  FreeIOBuffer (&buffer);
+  return;
+}
+
 int PrintControllerOutput () {
 
@@ -581,5 +624,5 @@
   InitIOBuffer (&buffer, 0x100);
 
-  // XXX lock the host table? SerialThreadLock ();
+  // XXX lock the host table? no: that would risk a dead lock between client and controller threads:
   gprint (GP_ERR, "pcontrol restarted, reloading hosts\n");
   for (i = 0; i < Nhosts; i++) {
@@ -588,5 +631,4 @@
     status = ControllerCommand (command, CONTROLLER_PROMPT, &buffer);
   }
-  // SerialThreadUnlock ();
 
   if (status) gwrite (buffer.buffer, 1, buffer.Nbuffer, GP_LOG);
Index: /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/InputQueue.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/InputQueue.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/InputQueue.c	(revision 23594)
@@ -1,3 +1,4 @@
 # include "pantasks.h"
+// we use fprintf for DEBUG statements to avoid deadlocking issues
 
 static int Ninputs = 0;
@@ -23,6 +24,8 @@
 void AddNewInput (char *input) {
 
+  // XXX define the InputMutex
   SerialThreadLock ();
-  if (DEBUG) gprint (GP_LOG, "adding a new input (%s)\n", input);
+
+  if (DEBUG) fprintf (stderr, "adding a new input (%s)\n", input);
   inputs[Ninputs] = input;
   Ninputs ++;
@@ -31,5 +34,5 @@
     REALLOCATE (inputs, char *, NINPUTS);
   }
-  if (DEBUG) gprint (GP_LOG, "done new input (%s)\n", input);
+  if (DEBUG) fprintf (stderr, "done new input (%s)\n", input);
   SerialThreadUnlock ();
 }
@@ -40,5 +43,7 @@
   int i, j;
 
-  if (DEBUG) gprint (GP_LOG, "deleting an input (%s)\n", input);
+  if (DEBUG) fprintf (stderr, "deleting an input (%s)\n", input);
+
+  // XXX lock here
   for (i = 0; i < Ninputs; i++) {
     if (inputs[i] == input) {
@@ -52,9 +57,11 @@
 	REALLOCATE (inputs, char *, NINPUTS);
       }
-      if (DEBUG) gprint (GP_LOG, "deleted an input\n");
+      // XXX unlock here
+      if (DEBUG) fprintf (stderr, "deleted an input\n");
       return TRUE;
     }
   }
   // did not find the input
+  // XXX unlock here
   return FALSE;
 }
@@ -66,12 +73,17 @@
   char *input, *line, *outline, tmp;
 
-  if (Ninputs < 1) return;
+  // XXX lock here
+  if (Ninputs < 1) {
+    // XXX unlock here
+    return;
+  }
 
   input = inputs[0];
-  if (DEBUG) gprint (GP_LOG, "got an input (%s)\n", input);
+  if (DEBUG) fprintf (stderr, "got an input (%s)\n", input);
   
   Nbytes = snprintf (&tmp, 0, "input %s", input);
   ALLOCATE (line, char, Nbytes + 1);
   snprintf (line, Nbytes + 1, "input %s", input);
+  // XXX unlock here
 
   status = command (line, &outline, TRUE);
Index: /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/JobIDOps.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/JobIDOps.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/JobIDOps.c	(revision 23594)
@@ -1,5 +1,5 @@
 # include "pantasks.h"
 
-# define MAX_N_JOBS 1000
+# define MAX_N_JOBS 10000
 static char *JobIDList;
 static int   JobIDPtr;
Index: /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/JobOps.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/JobOps.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/JobOps.c	(revision 23594)
@@ -88,24 +88,4 @@
   }
 
-  /* check if controller is running */
-  status = CheckControllerStatus ();
-  if (!status) {
-    return;
-  }
-
-  sprintf (command, "jobstack busy");
-  InitIOBuffer (&buffer, 0x100);
-
-  SerialThreadLock ();
-  status = ControllerCommand (command, CONTROLLER_PROMPT, &buffer);
-  SerialThreadUnlock ();
-
-  if (status) {
-    gprint (GP_LOG, " jobs currently running remotely:\n");
-    gwrite (buffer.buffer, 1, buffer.Nbuffer, GP_LOG);
-  } else {
-    gprint (GP_LOG, "controller is not responding\n");
-  }
-  FreeIOBuffer (&buffer);
   return;
 }
@@ -120,4 +100,9 @@
 
   job[0].JobID = NextJobID ();
+  if (job[0].JobID < 0) {
+    free (job);
+    return NULL;
+  }
+
   job[0].pid = 0;
   job[0].mode = JOB_LOCAL;
@@ -169,4 +154,8 @@
     REALLOCATE (jobs, Job *, NJOBS);
   }
+
+  /* increment job counters */
+  task[0].Npending ++;
+
   return (jobs[Njobs-1]);
 }
@@ -265,9 +254,14 @@
 int SubmitJob (Job *job) {
 
+  int status;
+
   if (job[0].mode == JOB_LOCAL) {
     if (DEBUG) fprintf (stderr, "submit job: (%zx) %d of %d\n", (size_t) job[0].stdout_buff.buffer, job[0].stdout_buff.Nbuffer, job[0].stdout_buff.Nalloc); 
-    SubmitLocalJob (job);
+    status = SubmitLocalJob (job);
   } else {
-    SubmitControllerJob (job);
+    status = SubmitControllerJob (job);
+  }
+  if (!status) {
+    return FALSE;
   }
 
@@ -285,7 +279,5 @@
   if (job[0].mode == JOB_LOCAL) {
     CheckLocalJob (job);
-  } else {
-    /* controller jobs are now checked en masse by CheckController */
-    /* CheckControllerJob (job); */
+    /* controller jobs are checked en masse by CheckController */
   }
   return (job[0].state);
Index: /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/ListenClients.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/ListenClients.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/ListenClients.c	(revision 23594)
@@ -2,4 +2,5 @@
 # define DEBUG 0
 
+// XXX make the calling functions thread-safe
 static int NCLIENTS;
 static int Nclients;
@@ -9,8 +10,10 @@
 void InitClients () {
 
+  ClientLock();
   Nclients = 0;
   NCLIENTS = 10;
   ALLOCATE (clients, int, NCLIENTS);
   ALLOCATE (buffers, IOBuffer *, NCLIENTS);
+  ClientUnlock();
 }
 
@@ -18,5 +21,6 @@
 void AddNewClient (int client) {
 
-  if (DEBUG) gprint (GP_LOG, "adding a new client (%d)\n", client);
+  ClientLock();
+  if (DEBUG) fprintf (stderr, "adding a new client (%d)\n", client);
   clients[Nclients] = client;
   ALLOCATE (buffers[Nclients], IOBuffer, 1);
@@ -28,4 +32,5 @@
     REALLOCATE (buffers, IOBuffer *, NCLIENTS);
   }
+  ClientUnlock();
 }
 
@@ -35,5 +40,6 @@
   int i, j;
 
-  if (DEBUG) gprint (GP_LOG, "deleting a client (%d)\n", client);
+  ClientLock();
+  if (DEBUG) fprintf (stderr, "deleting a client (%d)\n", client);
   for (i = 0; i < Nclients; i++) {
     if (clients[i] == client) {
@@ -51,8 +57,10 @@
 	REALLOCATE (buffers, IOBuffer *, NCLIENTS);
       }
+      ClientUnlock();
       return TRUE;
     }
   }
   // did not find the client
+  ClientUnlock();
   return FALSE;
 }
@@ -70,15 +78,7 @@
   gprintInit ();  // each thread needs to init the printing system
 
-  // define server output log files
-  if (VarConfig ("PANTASKS_SERVER_STDOUT", "%s", log_stdout) != NULL) {
-    gprintSetFileThisThread (GP_LOG, log_stdout);
-  } else {
-    strcpy (log_stdout, "stdout");
-  }
-  if (VarConfig ("PANTASKS_SERVER_STDERR", "%s", log_stderr) != NULL) {
-    gprintSetFileThisThread (GP_ERR, log_stderr);
-  } else {
-    strcpy (log_stderr, "stderr");
-  }
+  /* set buffers for the output for this client */
+  gprintSetBuffer (GP_LOG);
+  gprintSetBuffer (GP_ERR);
 
   while (1) {
@@ -90,7 +90,9 @@
 
     /* place all of the clients in the fdSet */
-    Ncurrent = Nclients;
     Nmax = 0;
     FD_ZERO (&fdSet);
+    ClientLock();
+    Ncurrent = Nclients;
+    ClientUnlock();
     for (i = 0; i < Ncurrent; i++) {
       Nmax = MAX (Nmax, clients[i]);
@@ -100,5 +102,5 @@
 
     /* block until we have some data on the pipes (or timeout) */
-    if (DEBUG) gprint (GP_ERR, "listening to %d clients\n", Ncurrent);
+    if (DEBUG) fprintf (stderr, "listening to %d clients\n", Ncurrent);
     status = select (Nmax, &fdSet, NULL, NULL, &timeout);
     if (status == -1) {
@@ -122,11 +124,11 @@
       if ((Nread == 0) || (Nread == -2)) {
 	/* error: do something */
-	if (DEBUG && (Nread == 0)) gprint (GP_ERR, "socket is closed\n");
-	if (DEBUG && (Nread == -2)) gprint (GP_ERR, "error reading from socket\n");
+	if (DEBUG && (Nread == 0)) fprintf (stderr, "socket is closed\n");
+	if (DEBUG && (Nread == -2)) fprintf (stderr, "error reading from socket\n");
 	DeleteClient (clients[i]);
-	continue;
+	break;  // the other thread could also have modified the list; restart with new Ncurrent
       }
 
-      if (DEBUG) gprint (GP_ERR, "read %d total bytes\n", buffers[i][0].Nbuffer);
+      if (DEBUG) fprintf (stderr, "read %d total bytes\n", buffers[i][0].Nbuffer);
 
       /* see if we have a complete message waiting; if not, keep waiting for messages */
@@ -141,10 +143,8 @@
       if (*line) {
 
-	/* set buffers for the output for this client */
-	gprintSetBuffer (GP_LOG);
-	gprintSetBuffer (GP_ERR);
-
 	/* run the command, return the exit status */
+	CommandLock();
 	status = multicommand (line);
+	CommandUnlock();
 	SendMessage (clients[i], "STATUS %d", status);
 
@@ -156,4 +156,5 @@
 	  SendMessageFixed (clients[i], 0, "");
 	}	  
+	FlushIOBuffer (outbuffer);
 
 	// return the stdout messages first
@@ -164,8 +165,5 @@
 	  SendMessageFixed (clients[i], 0, "");
 	}	  
-	
-	/* clear and reset the output buffers to their last output file names */
-	gprintSetFileAllThreads (GP_LOG, NULL);
-	gprintSetFileAllThreads (GP_ERR, NULL);
+	FlushIOBuffer (outbuffer);
       }
       free (line);
Index: /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/LocalJob.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/LocalJob.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/LocalJob.c	(revision 23594)
@@ -127,4 +127,9 @@
 
   pid = fork ();
+  if (pid == -1) {
+    gprint_syserror (GP_ERR, errno, "error starting local job: ");
+    goto pipe_error;
+  }
+
   if (!pid) { /* must be child process */
     if (VerboseMode()) gprint (GP_ERR, "starting local job\n");
Index: /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/Makefile
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/Makefile	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/Makefile	(revision 23594)
@@ -34,6 +34,5 @@
 $(SRC)/pantasks.$(ARCH).o \
 $(SRC)/thread_locks.$(ARCH).o \
-$(SRC)/job_threads.$(ARCH).o \
-$(SRC)/task_threads.$(ARCH).o \
+$(SRC)/jobs_and_tasks_thread.$(ARCH).o \
 $(SRC)/controller_threads.$(ARCH).o 
 
@@ -41,6 +40,4 @@
 $(SRC)/pantasks_server.$(ARCH).o \
 $(SRC)/server_run.$(ARCH).o \
-$(SRC)/server_load.$(ARCH).o \
-$(SRC)/InputQueue.$(ARCH).o \
 $(SRC)/ListenClients.$(ARCH).o \
 $(SRC)/server.$(ARCH).o \
@@ -49,8 +46,6 @@
 $(SRC)/CheckPassword.$(ARCH).o \
 $(SRC)/thread_locks.$(ARCH).o \
-$(SRC)/job_threads.$(ARCH).o \
-$(SRC)/task_threads.$(ARCH).o \
-$(SRC)/controller_threads.$(ARCH).o \
-$(SRC)/input_threads.$(ARCH).o
+$(SRC)/jobs_and_tasks_thread.$(ARCH).o \
+$(SRC)/controller_threads.$(ARCH).o
 
 funcs = \
@@ -66,5 +61,4 @@
 
 cmds = \
-$(SRC)/pulse.$(ARCH).o \
 $(SRC)/status.$(ARCH).o \
 $(SRC)/flush.$(ARCH).o \
Index: /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/controller.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/controller.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/controller.c	(revision 23594)
@@ -50,5 +50,7 @@
   }
 
+  ControlLock(__func__);
   status = (*func)(argc - 1, argv + 1);
+  ControlUnlock(__func__);
   return (status);
 }
Index: /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/controller_host.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/controller_host.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/controller_host.c	(revision 23594)
@@ -15,4 +15,5 @@
 
   if (argc != 3) goto usage;
+  if (max_threads && strcasecmp (argv[1], "ADD")) goto usage;
 
   /* start controller connection (if needed) */
@@ -27,7 +28,5 @@
   if (!strcasecmp (argv[1], "ADD")) {
     AddHost (argv[2], max_threads);
-  } else {
-    if (max_threads) goto usage;
-  }
+  } 
 
   if (!strcasecmp (argv[1], "DELETE")) {
@@ -42,7 +41,5 @@
   InitIOBuffer (&buffer, 0x100);
 
-  SerialThreadLock ();
   status = ControllerCommand (command, CONTROLLER_PROMPT, &buffer);
-  SerialThreadUnlock ();
 
   if (status) gwrite (buffer.buffer, 1, buffer.Nbuffer, GP_LOG);
@@ -57,8 +54,2 @@
   return (FALSE);
 }
-
-/* should I keep an internal host table so I can reload the 
-   hosts if the controller exits?
-
-   alternatively, that could be a user-level choice
-*/
Index: /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/controller_jobstack.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/controller_jobstack.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/controller_jobstack.c	(revision 23594)
@@ -24,7 +24,5 @@
   InitIOBuffer (&buffer, 0x100);
 
-  SerialThreadLock ();
   status = ControllerCommand (command, CONTROLLER_PROMPT, &buffer);
-  SerialThreadUnlock ();
 
   if (status) {
Index: /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/controller_status.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/controller_status.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/controller_status.c	(revision 23594)
@@ -23,7 +23,5 @@
   InitIOBuffer (&buffer, 0x100);
 
-  SerialThreadLock ();
   status = ControllerCommand (command, CONTROLLER_PROMPT, &buffer);
-  SerialThreadUnlock ();
 
   if (status) {
Index: /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/controller_threads.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/controller_threads.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/controller_threads.c	(revision 23594)
@@ -34,8 +34,12 @@
 
     // one run of the task checker
-    SerialThreadLock ();
+    ControlLock(__func__);
     CheckController ();
+    ControlUnlock(__func__);
+
+    ControlLock(__func__);
     CheckControllerOutput ();
-    SerialThreadUnlock ();
+    ControlUnlock(__func__);
+
     if (VerboseMode() == 2) fprintf (stderr, "C");
     // fprintf (stderr, "**** C ****");
Index: /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/controller_verbose.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/controller_verbose.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/controller_verbose.c	(revision 23594)
@@ -24,7 +24,5 @@
   InitIOBuffer (&buffer, 0x100);
 
-  SerialThreadLock ();
   status = ControllerCommand (command, CONTROLLER_PROMPT, &buffer);
-  SerialThreadUnlock ();
 
   if (status) {
Index: /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/delete.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/delete.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/delete.c	(revision 23594)
@@ -11,7 +11,9 @@
     JobID = atoi (argv[2]);
 
+    JobTaskLock();
     job = FindJob (JobID);
     if (job == NULL) {
       gprint (GP_LOG, "job not found\n");
+      JobTaskUnlock();
       return (TRUE);
     }
@@ -21,4 +23,5 @@
 	job[0].state = JOB_HUNG;
 	if (VerboseMode()) gprint (GP_LOG, "child process %d is hung, cannot kill\n", job[0].pid);
+	JobTaskUnlock();
 	return (FALSE);
       }
@@ -27,4 +30,5 @@
 	job[0].state = JOB_HUNG;
 	if (VerboseMode()) gprint (GP_LOG, "child process %d is hung, cannot kill\n", job[0].pid);
+	JobTaskUnlock();
 	return (FALSE);
       }
@@ -32,4 +36,5 @@
     DeleteJob (job);
     gprint (GP_LOG, "job removed\n");
+    JobTaskUnlock();
     return (TRUE);
   }
@@ -41,7 +46,7 @@
 
 usage:
-    gprint (GP_ERR, "USAGE: delete job (JobID)\n");
-    gprint (GP_ERR, "USAGE: delete task (TaskName)\n");
-    return (FALSE);
+  gprint (GP_ERR, "USAGE: delete job (JobID)\n");
+  gprint (GP_ERR, "USAGE: delete task (TaskName)\n");
+  return (FALSE);
 }
 
Index: /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/flush.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/flush.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/flush.c	(revision 23594)
@@ -6,5 +6,7 @@
 
   if (!strcasecmp (argv[1], "jobs")) {
+    JobTaskLock();
     FlushJobs ();
+    JobTaskUnlock();
     return (TRUE);
   }
Index: /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/init.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/init.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/init.c	(revision 23594)
@@ -18,5 +18,4 @@
 int halt            PROTO((int, char **));
 int flush_jobs      PROTO((int, char **));
-int pulse           PROTO((int, char **));
 int showtask        PROTO((int, char **));
 int status_sys      PROTO((int, char **));
@@ -40,5 +39,4 @@
   {1, "options",    task_options,  "define optional variables associated with the job task"},
   {1, "periods",    task_periods,  "define time scales for a task"},
-  {1, "pulse",      pulse,         "set the scheduler update period"},
   {1, "run",        run,           "run the scheduler"},
   {1, "flush",      flush_jobs,    "flush all jobs from the queue"},
Index: /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/init_server.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/init_server.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/init_server.c	(revision 23594)
@@ -14,5 +14,4 @@
 int task_stdout     PROTO((int, char **));
 int task_stderr     PROTO((int, char **));
-int pulse           PROTO((int, char **));
 int flush_jobs      PROTO((int, char **));
 int status_server   PROTO((int, char **));
@@ -41,5 +40,4 @@
   {1, "npending",   task_npending, "define maximum number of outstanding jobs for a task"},
   {1, "periods",    task_periods,  "define time scales for a task"},
-  {1, "pulse",      pulse,         "set the scheduler update period"},
   {1, "flush",      flush_jobs,    "flush all jobs from the queue"},
   {1, "server",     server,        "server-specific commands"},
@@ -67,5 +65,4 @@
   InitJobs ();
   InitJobIDs ();
-  InitInputs ();
 
   for (i = 0; i < sizeof (cmds) / sizeof (Command); i++) {
@@ -78,4 +75,3 @@
   FreeJobs ();
   FreeJobIDs ();
-  FreeInputs ();
 }
Index: /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/jobs_and_tasks_thread.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/jobs_and_tasks_thread.c	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/jobs_and_tasks_thread.c	(revision 23594)
@@ -0,0 +1,95 @@
+# include "pantasks.h"
+
+void ResetTaskTimers ();
+
+static int CheckTasksRun = FALSE;
+static int CheckJobsRun = FALSE;
+
+void CheckJobsSetState (int state) {
+  CheckJobsRun = state;
+}
+int CheckJobsGetState () {
+  return (CheckJobsRun);
+}
+
+void CheckTasksSetState (int state) {
+  if (state && !CheckTasksRun) {
+    ResetTaskTimers ();
+  }
+  CheckTasksRun = state;
+}
+int CheckTasksGetState () {
+  return (CheckTasksRun);
+}
+
+void *CheckJobsAndTasksThread (void *data) {
+
+  char log_stdout[128], log_stderr[128];
+  float job_timeout, task_timeout, next_timeout;
+
+  gprintInit ();  // each thread needs to init the printing system
+
+  // define server output log files
+  if (VarConfig ("PANTASKS_SERVER_STDOUT", "%s", log_stdout) != NULL) {
+      gprintSetFileThisThread (GP_LOG, log_stdout);
+  }
+  if (VarConfig ("PANTASKS_SERVER_STDERR", "%s", log_stderr) != NULL) {
+      gprintSetFileThisThread (GP_ERR, log_stderr);
+  }
+
+  while (1) {
+
+    // one run of the task checker
+    task_timeout = 0.25;
+    if (CheckTasksRun) {
+      task_timeout = CheckTasks ();
+      if (VerboseMode() == 2) fprintf (stderr, "T");
+    }
+
+    // one run of the task checker
+    job_timeout = 0.25;
+    if (CheckJobsRun) {
+      job_timeout = CheckJobs ();
+      if (VerboseMode() == 2) fprintf (stderr, "J");
+    }
+
+    // job_timeout, task_timeout is time until next job,task is ready
+    // sleep more-or-less that long (but no longer than 250msec)
+    next_timeout = MIN (job_timeout, task_timeout);
+    next_timeout = MIN (next_timeout, 0.25);
+    if (next_timeout > 0.001) {
+      usleep ((int)(1000000*next_timeout)); // allow other threads a chance to run
+    }      
+  }
+}
+
+// I sleep for a small amount of time here, based on how long until the next expected
+// 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/pantasks/kill.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/kill.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/kill.c	(revision 23594)
@@ -12,7 +12,9 @@
   JobID = atoi (argv[1]);
 
+  JobTaskLock();
   job = FindJob (JobID);
   if (job == NULL) {
     gprint (GP_LOG, "job not found\n");
+    JobTaskUnlock();
     return (TRUE);
   }
@@ -22,4 +24,5 @@
       job[0].state = JOB_HUNG;
       if (VerboseMode()) gprint (GP_LOG, "child process %d is hung, cannot kill\n", job[0].pid);
+      JobTaskUnlock();
       return (FALSE);
     }
@@ -28,4 +31,5 @@
       job[0].state = JOB_HUNG;
       if (VerboseMode()) gprint (GP_LOG, "child process %d is hung, cannot kill\n", job[0].pid);
+      JobTaskUnlock();
       return (FALSE);
     }
@@ -33,4 +37,5 @@
   DeleteJob (job);
   gprint (GP_LOG, "job removed\n");
+  JobTaskUnlock();
   return (TRUE);
 }
Index: /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/notes.txt
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/notes.txt	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/notes.txt	(revision 23594)
@@ -1,2 +1,126 @@
+
+stdout_cntl:
+  GetJobOutout
+    CheckControllerJob
+      CheckController
+        controller_threads (controlThread)
+  StartController
+  ControllerCommand
+    CheckController
+      controller_threads (controlThread)
+    controller_check (clientThread)
+    controller_host (clientThread)
+    controller_jobstack (clientThread)
+    controller_run (clientThread)
+    controller_status (clientThread)
+    controller_verbose (clientThread)
+    DeleteControllerJob
+      CheckControllerJob
+        CheckController
+	  controller_threads (controlThread)
+    CheckControllerJobStatus
+      CheckControllerJob
+        CheckController
+	  controller_threads (controlThread)
+    SubmitControllerJob
+      SubmitJob
+        CheckTasks (JobTaskThread)
+    PrintControllerBusyJobs
+    KillControllerJob
+    QuitController
+    RestartController    
+  CheckControllerOutput
+    controller_output (clientThread)
+    controller_threads (controlThread)
+
+
+
+
+
+
+
+20090322
+
+  We've been surviving with a slightly broken threading / locking
+  model.  I would like to clean it up.  Previously, the threads where
+  blocking at a very coarse level, and there were certain interactions
+  that were not thread-safe.  Here are my notes on re-working this:
+
+  * we have 6 threads:
+
+    * main (top-level parent) : after it spawns the 5 child threads,
+      this thread waits for new clients and adds them to the list of
+      active clients with 'AddNewClient'
+
+    * clientsThread (ListenClients): this thread is listening for
+      commands from the clients; when it receives a complete command,
+      it executes the command using 'multicommand'.
+
+    * tasksThread
+      CheckTasks
+	NextTask
+
+
+
+    * jobsThread
+
+    * controllerThread
+      CheckControllerStatus : race condition is irrelevant
+
+    * inputsThread
+      AddNewInput called by clientThread
+      (not sure this is really being used: 
+      	   server input (filename) calls 'input'
+	   server module (filename) calls 'module'
+
+  * functions which need to be thread-safe:
+    * AddNewClient
+    * gprint and supporting functions must be thread safe (extensively
+      used...)
+
+
+* race condition is irrelevant for this variable
+static int ControllerStatus = FALSE;
+
+static int stdin_cntl, stdout_cntl, stderr_cntl;
+GetJobOutput -> CheckControllerJob -> CheckController -> controllerThread
+ControllerCommand -> controllerThread / clientThread
+CheckControllerOutput -> controllerThread / clientThread
+StartController -> controllerThread / clientThread
+StopController
+
+
+
+static IOBuffer stdout_buffer;
+static IOBuffer stderr_buffer;
+static int ControllerPID = 0;
+
+/* local static variables to track the controller host properties */
+static Host *hosts = NULL;
+static int Nhosts = 0;
+static int NHOSTS = 0;
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
 
 PanTasks Client / Server design
Index: /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/pantasks.c.in
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/pantasks.c.in	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/pantasks.c.in	(revision 23594)
@@ -10,6 +10,5 @@
 void program_init (int *argc, char **argv) {
   
-  pthread_t jobsThread;
-  pthread_t tasksThread;
+  pthread_t JobsAndTasksThread;
   pthread_t controllerThread;
 
@@ -50,7 +49,8 @@
 
   /* start up the background threads here */
-  pthread_create (&tasksThread,      NULL, &CheckTasksThread, 	   NULL);
-  pthread_create (&jobsThread,       NULL, &CheckJobsThread, 	   NULL);
-  pthread_create (&controllerThread, NULL, &CheckControllerThread, NULL);
+  // pthread_create (&tasksThread,      NULL, &CheckTasksThread, 	   NULL);
+  // pthread_create (&jobsThread,       NULL, &CheckJobsThread, 	   NULL);
+  pthread_create (&JobsAndTasksThread, NULL, &CheckJobsAndTasksThread, NULL);
+  pthread_create (&controllerThread,   NULL, &CheckControllerThread,   NULL);
   return;
 }
Index: /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/pantasks_server.c.in
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/pantasks_server.c.in	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/pantasks_server.c.in	(revision 23594)
@@ -17,7 +17,5 @@
   
   char log_stdout[128], log_stderr[128];
-  pthread_t jobsThread;
-  pthread_t tasksThread;
-  pthread_t inputsThread;
+  pthread_t JobsAndTasksThread;
   pthread_t clientsThread;
   pthread_t controllerThread;
@@ -63,9 +61,7 @@
 
   /* start up the background threads here */
-  pthread_create (&clientsThread,    NULL, &ListenClients,    	   NULL);
-  pthread_create (&tasksThread,      NULL, &CheckTasksThread, 	   NULL);
-  pthread_create (&jobsThread,       NULL, &CheckJobsThread,  	   NULL);
-  pthread_create (&controllerThread, NULL, &CheckControllerThread, NULL);
-  pthread_create (&inputsThread,     NULL, &CheckInputsThread,     NULL);
+  pthread_create (&clientsThread,       NULL, &ListenClients,    	NULL);
+  pthread_create (&JobsAndTasksThread,  NULL, &CheckJobsAndTasksThread, NULL);
+  pthread_create (&controllerThread, 	NULL, &CheckControllerThread,   NULL);
 
   /* in this loop, we listen for incoming connections, validate, and
Index: /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/server.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/server.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/server.c	(revision 23594)
@@ -5,5 +5,4 @@
 int input      	    PROTO((int, char **));
 int module     	    PROTO((int, char **));
-int server_load	    PROTO((int, char **));
 int server_run	    PROTO((int, char **));
 int server_stop	    PROTO((int, char **));
@@ -20,5 +19,4 @@
   {1, "input",  input,  "load input file on server"},
   {1, "module", module, "load module file on server"},
-  {1, "load",   server_load, "load input file on server"},
   {1, "run",    server_run,  "run scheduler"},
   {1, "stop",   server_stop, "stop scheduler"},
Index: /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/server_run.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/server_run.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/server_run.c	(revision 23594)
@@ -11,5 +11,4 @@
   CheckJobsSetState (TRUE);
   CheckControllerSetState (TRUE);
-  CheckInputsSetState (TRUE);
   return (TRUE);
 }
@@ -23,7 +22,4 @@
 
   CheckTasksSetState (FALSE);
-  // CheckJobsSetState (FALSE);
-  // CheckControllerSetState (FALSE);
-  CheckInputsSetState (FALSE);
   return (TRUE);
 }
@@ -39,5 +35,4 @@
   CheckJobsSetState (FALSE);
   CheckControllerSetState (FALSE);
-  CheckInputsSetState (FALSE);
   return (TRUE);
 }
Index: /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/showtask.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/showtask.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/showtask.c	(revision 23594)
@@ -8,5 +8,8 @@
   }
 
+  JobTaskLock();
   ShowTask (argv[1]);
+  JobTaskUnlock();
+
   return (TRUE);
 }
Index: /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/status_server.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/status_server.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/status_server.c	(revision 23594)
@@ -11,5 +11,7 @@
   if ((N = get_argument (argc, argv, "-tasks"))) {
     remove_argument (N, &argc, argv);
+    JobTaskLock();
     ListTasks (FALSE);
+    JobTaskUnlock();
     return (TRUE);
   }
@@ -17,5 +19,7 @@
   if ((N = get_argument (argc, argv, "-taskinfo"))) {
     remove_argument (N, &argc, argv);
+    JobTaskLock();
     ListTasks (TRUE);
+    JobTaskUnlock();
     return (TRUE);
   }
@@ -23,4 +27,5 @@
   if ((N = get_argument (argc, argv, "-taskstats"))) {
     remove_argument (N, &argc, argv);
+    JobTaskLock();
     if (argc == 2) {
       ListTaskStats (argv[N]);
@@ -28,4 +33,5 @@
       ListTaskStats (NULL);
     }      
+    JobTaskUnlock();
     return (TRUE);
   }
@@ -33,4 +39,5 @@
   if ((N = get_argument (argc, argv, "-taskstatsreset"))) {
     remove_argument (N, &argc, argv);
+    JobTaskLock();
     if (argc == 2) {
       ResetTaskStats (argv[N]);
@@ -38,4 +45,5 @@
       ResetTaskStats (NULL);
     }      
+    JobTaskUnlock();
     return (TRUE);
   }
@@ -56,6 +64,14 @@
     gprint (GP_LOG, " Controller is stopped\n");
   }
+
+  JobTaskLock();
   ListTasks (FALSE);
   ListJobs ();
+  JobTaskUnlock();
+
+  ControlLock(__func__);
+  PrintControllerBusyJobs();
+  ControlUnlock(__func__);
+
   return (TRUE);
 
Index: /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/task.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/task.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/task.c	(revision 23594)
@@ -11,4 +11,5 @@
   if (argc != 2) goto usage;
 
+  JobTaskLock();
   task = FindTask (argv[1]);
   if (task == NULL) { /**** new task ****/
@@ -18,4 +19,6 @@
     SetNewTask (task);
   }
+  JobTaskUnlock();
+
   /* While a task is being defined, it is removed from the task list.  The new task is added to the task list
      when the definition process is complete.  
@@ -55,6 +58,4 @@
 
       case TASK_END:
-	/* I need to add in a test here to see if all task elements 
-	   have been defined.  delete the task if not */
 	free (input);
 	/* validate the new task: all mandatory elements defined? */ 
@@ -63,5 +64,7 @@
 	  return (FALSE);
 	}
+	JobTaskLock();
 	RegisterNewTask ();
+	JobTaskUnlock();
 	return (TRUE);
 	break;
Index: /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/task_active.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/task_active.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/task_active.c	(revision 23594)
@@ -7,7 +7,9 @@
   if (argc != 2) goto usage;
 
+  JobTaskLock();
   task = GetNewTask ();
   if (task == NULL) {
     gprint (GP_ERR, "ERROR: not defining or running a task\n");
+    JobTaskUnlock();
     return (FALSE);
   }
@@ -15,12 +17,15 @@
   if (!strcasecmp (argv[1], "true")) {
     task[0].active = TRUE;
+    JobTaskUnlock();
     return (TRUE);
   }
   if (!strcasecmp (argv[1], "false")) {
     task[0].active = FALSE;
+    JobTaskUnlock();
     return (TRUE);
   }
 
   gprint (GP_ERR, "ERROR: invalid option: %s\n", argv[1]);
+  JobTaskUnlock();
   return (FALSE);
 
Index: /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/task_command.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/task_command.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/task_command.c	(revision 23594)
@@ -12,4 +12,5 @@
   }
 
+  JobTaskLock();
   task = GetNewTask ();
   if (task == NULL) {
@@ -17,4 +18,5 @@
     if (task == NULL) {
       gprint (GP_ERR, "ERROR: not defining or running a task\n");
+      JobTaskUnlock();
       return (FALSE);
     }
@@ -35,4 +37,5 @@
     task[0].argv[i] = strcreate (argv[i+1]);
   }
+  JobTaskUnlock();
   return (TRUE);
 }
Index: /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/task_host.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/task_host.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/task_host.c	(revision 23594)
@@ -22,8 +22,10 @@
 
   task = GetNewTask ();
+  JobTaskLock();
   if (task == NULL) {
     task = GetActiveTask ();
     if (task == NULL) {
       gprint (GP_ERR, "ERROR: not defining or running a task\n");
+      JobTaskUnlock();
       return (FALSE);
     }
@@ -34,7 +36,11 @@
   task[0].host = NULL;
 
-  if (!strcasecmp (argv[1], "LOCAL")) return (TRUE);
+  if (!strcasecmp (argv[1], "LOCAL")) {
+    JobTaskUnlock();
+    return (TRUE);
+  }
 
   task[0].host = strcreate (argv[1]);
+  JobTaskUnlock();
   return (TRUE);
 }
Index: /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/task_macros.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/task_macros.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/task_macros.c	(revision 23594)
@@ -22,7 +22,9 @@
   }
 
+  JobTaskLock();
   task = GetNewTask ();
   if (task == NULL) {
     gprint (GP_ERR, "ERROR: not defining or running a task\n");
+    JobTaskUnlock();
     return (FALSE);
   }
@@ -135,4 +137,5 @@
 	free (input);
 	REALLOCATE (macro[0].line, char *, MAX (1, macro[0].Nlines));
+	JobTaskUnlock();
 	return (TRUE);
       }
@@ -148,4 +151,5 @@
     }
   }
+  JobTaskUnlock();
   return (TRUE);
 }
Index: /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/task_nmax.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/task_nmax.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/task_nmax.c	(revision 23594)
@@ -7,11 +7,14 @@
   if (argc != 2) goto usage;
 
+  JobTaskLock();
   task = GetNewTask ();
   if (task == NULL) {
     gprint (GP_ERR, "ERROR: not defining or running a task\n");
+    JobTaskUnlock();
     return (FALSE);
   }
 
   task[0].Nmax = atoi (argv[1]);
+  JobTaskUnlock();
   return (TRUE);
 
@@ -27,11 +30,14 @@
   if (argc != 2) goto usage;
 
+  JobTaskLock();
   task = GetNewTask ();
   if (task == NULL) {
     gprint (GP_ERR, "ERROR: not defining or running a task\n");
+    JobTaskUnlock();
     return (FALSE);
   }
 
   task[0].NpendingMax = atoi (argv[1]);
+  JobTaskUnlock();
   return (TRUE);
 
Index: /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/task_options.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/task_options.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/task_options.c	(revision 23594)
@@ -12,4 +12,5 @@
   }
 
+  JobTaskLock();
   task = GetNewTask ();
   if (task == NULL) {
@@ -17,4 +18,5 @@
     if (task == NULL) {
       gprint (GP_ERR, "ERROR: not defining or running a task\n");
+      JobTaskUnlock();
       return (FALSE);
     }
@@ -35,4 +37,5 @@
     task[0].optv[i] = strcreate (argv[i+1]);
   }
+  JobTaskUnlock();
   return (TRUE);
 }
Index: /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/task_periods.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/task_periods.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/task_periods.c	(revision 23594)
@@ -40,4 +40,5 @@
   }
 
+  JobTaskLock();
   task = GetNewTask ();
   if (task == NULL) {
@@ -45,4 +46,5 @@
     if (task == NULL) {
       gprint (GP_ERR, "ERROR: not defining or running a task\n");
+      JobTaskUnlock();
       return (FALSE);
     }
@@ -53,4 +55,5 @@
   if (Timeout) task[0].timeout_period = TimeoutValue;
 
+  JobTaskUnlock();
   return (TRUE);
 }
Index: /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/task_stdout.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/task_stdout.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/task_stdout.c	(revision 23594)
@@ -11,4 +11,5 @@
   }
 
+  JobTaskLock();
   task = GetNewTask ();
   if (task == NULL) {
@@ -16,4 +17,5 @@
     if (task == NULL) {
       gprint (GP_ERR, "ERROR: not defining or running a task\n");
+      JobTaskUnlock();
       return (FALSE);
     }
@@ -21,4 +23,5 @@
   if (task[0].stdout_dump != NULL) free (task[0].stdout_dump);
   task[0].stdout_dump = strcreate (argv[1]);
+  JobTaskUnlock();
   return (TRUE);
 }
@@ -34,4 +37,5 @@
   }
 
+  JobTaskLock();
   task = GetNewTask ();
   if (task == NULL) {
@@ -39,4 +43,5 @@
     if (task == NULL) {
       gprint (GP_ERR, "ERROR: not defining or running a task\n");
+      JobTaskUnlock();
       return (FALSE);
     }
@@ -44,4 +49,5 @@
   if (task[0].stderr_dump != NULL) free (task[0].stderr_dump);
   task[0].stderr_dump = strcreate (argv[1]);
+  JobTaskUnlock();
   return (TRUE);
 }
Index: /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/task_trange.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/task_trange.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/task_trange.c	(revision 23594)
@@ -12,7 +12,9 @@
     if (argc != 1) goto usage;
 
+    JobTaskLock();
     task = GetNewTask ();
     if (task == NULL) {
       gprint (GP_ERR, "ERROR: not defining or running a task\n");
+      JobTaskUnlock();
       return (FALSE);
     }
@@ -20,4 +22,5 @@
     task[0].Nranges = 0;
     REALLOCATE (task[0].ranges, TimeRange, 1);
+    JobTaskUnlock();
     return (TRUE);
   } 
@@ -40,10 +43,4 @@
 
   if (argc != 3) goto usage;
-
-  task = GetNewTask ();
-  if (task == NULL) {
-    gprint (GP_ERR, "ERROR: not defining or running a task\n");
-    return (FALSE);
-  }
 
   /* test for Mon[@HH:MM:SS] - both must match */
@@ -83,4 +80,12 @@
 
 valid:
+  JobTaskLock();
+  task = GetNewTask ();
+  if (task == NULL) {
+    gprint (GP_ERR, "ERROR: not defining or running a task\n");
+    JobTaskUnlock();
+    return (FALSE);
+  }
+
   N = task[0].Nranges;
   task[0].Nranges ++;
@@ -88,4 +93,5 @@
   
   task[0].ranges[N] = range;
+  JobTaskUnlock();
   return (TRUE);
 
Index: /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/test/threadload.sh
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/test/threadload.sh	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/test/threadload.sh	(revision 23594)
@@ -0,0 +1,108 @@
+
+$hostname = `hostname`
+
+controller exit true
+# controller host add $hostname -threads 0
+controller host add $hostname -threads 3
+
+# a basic task which just runs 'sleep'
+task	       basic
+  command      ls -lRrt /tmp 
+  host         anyhost
+
+  periods      -poll 0.1
+  periods      -exec 0.5
+  periods      -timeout 20
+  npending 10
+  
+  stdout tmp.txt
+  stderr tmp.txt
+
+  task.exec
+    # echo "create command"
+  end
+
+  # success
+  task.exit    0
+    $Npass ++
+    # echo done sleep
+  end
+
+  # default exit status
+  task.exit    default
+    echo       "basic: exit status: $EXIT"
+  end
+
+  # operation times out?
+  task.exit    timeout
+    echo       "basic: timeout"
+  end
+end
+
+# a basic task which just runs 'sleep'
+task	       bigger
+  command      ls -lRrt /usr/bin
+  host         anyhost
+
+  periods      -poll 0.1
+  periods      -exec 0.5
+  periods      -timeout 20
+  npending 10
+  
+  stdout tmp.txt
+  stderr tmp.txt
+
+  task.exec
+    # echo "create command"
+  end
+
+  # success
+  task.exit    0
+    $Npass ++
+    # echo done sleep
+  end
+
+  # default exit status
+  task.exit    default
+    echo       "basic: exit status: $EXIT"
+  end
+
+  # operation times out?
+  task.exit    timeout
+    echo       "basic: timeout"
+  end
+end
+
+# a basic task which just runs 'sleep'
+task	       biggest
+  command      ls -lRrt /usr/lib
+  host         anyhost
+
+  periods      -poll 0.1
+  periods      -exec 0.5
+  periods      -timeout 20
+  npending 10
+  
+  stdout tmp.txt
+  stderr tmp.txt
+
+  task.exec
+    # echo "create command"
+  end
+
+  # success
+  task.exit    0
+    $Npass ++
+    # echo done sleep
+  end
+
+  # default exit status
+  task.exit    default
+    echo       "basic: exit status: $EXIT"
+  end
+
+  # operation times out?
+  task.exit    timeout
+    echo       "basic: timeout"
+  end
+end
Index: /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/test/threadload2.sh
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/test/threadload2.sh	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/test/threadload2.sh	(revision 23594)
@@ -0,0 +1,146 @@
+
+$hostname = `hostname`
+
+controller exit true
+# controller host add $hostname -threads 0
+controller host add $hostname -threads 3
+controller host add $hostname -threads 3
+controller host add $hostname -threads 3
+
+# a basic task which just runs 'sleep'
+task	       basic
+  command      ls -lRrt /tmp 
+  host         anyhost
+
+  periods      -poll 0.1
+  periods      -exec 0.5
+  periods      -timeout 20
+  npending 10
+  
+  stdout tmp.txt
+  stderr tmp.txt
+
+  task.exec
+    # echo "create command"
+  end
+
+  # success
+  task.exit    0
+    $Npass ++
+    # output out.dat
+    local i myvalue N
+    queuesize stdout -var N
+    for i 0 $N
+      queuepop stdout -var myvalue
+      # echo $myvalue
+    end
+    queuesize stderr -var N
+    for i 0 $N
+      queuepop stderr -var myvalue
+      # echo $myvalue
+    end
+    # output stdout
+  end
+
+  # default exit status
+  task.exit    default
+    echo       "basic: exit status: $EXIT"
+  end
+
+  # operation times out?
+  task.exit    timeout
+    echo       "basic: timeout"
+  end
+end
+
+# a basic task which just runs 'sleep'
+task	       bigger
+  command      ls -lRrt /usr/bin
+  host         anyhost
+
+  periods      -poll 0.1
+  periods      -exec 0.5
+  periods      -timeout 20
+  npending 10
+  
+  stdout tmp.txt
+  stderr tmp.txt
+
+  task.exec
+    # echo "create command"
+  end
+
+  # success
+  task.exit    0
+    $Npass ++
+    # output out.dat
+    local i myvalue N
+    queuesize stdout -var N
+    for i 0 $N
+      queuepop stdout -var myvalue
+      # echo $myvalue
+    end
+    queuesize stderr -var N
+    for i 0 $N
+      queuepop stderr -var myvalue
+      # echo $myvalue
+    end
+    # output stdout
+  end
+
+  # default exit status
+  task.exit    default
+    echo       "basic: exit status: $EXIT"
+  end
+
+  # operation times out?
+  task.exit    timeout
+    echo       "basic: timeout"
+  end
+end
+
+# a basic task which just runs 'sleep'
+task	       biggest
+  command      ls -lRrt /usr/lib
+  host         anyhost
+
+  periods      -poll 0.1
+  periods      -exec 0.5
+  periods      -timeout 20
+  npending 10
+  
+  stdout tmp.txt
+  stderr tmp.txt
+
+  task.exec
+    # echo "create command"
+  end
+
+  # success
+  task.exit    0
+    $Npass ++
+    # output out.dat
+    local i myvalue N
+    queuesize stdout -var N
+    for i 0 $N
+      queuepop stdout -var myvalue
+      # echo $myvalue
+    end
+    queuesize stderr -var N
+    for i 0 $N
+      queuepop stderr -var myvalue
+      # echo $myvalue
+    end
+    # output stdout
+  end
+
+  # default exit status
+  task.exit    default
+    echo       "basic: exit status: $EXIT"
+  end
+
+  # operation times out?
+  task.exit    timeout
+    echo       "basic: timeout"
+  end
+end
Index: /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/thread_locks.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/thread_locks.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pantasks/thread_locks.c	(revision 23594)
@@ -1,14 +1,52 @@
 # include "pantasks.h"
 
-/* this mutex is used by the serialized threads */
+/* mutex to lock Client table operations */
+static pthread_mutex_t ClientMutex = PTHREAD_MUTEX_INITIALIZER;
 
-static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
-
-void SerialThreadLock () {
-    pthread_mutex_lock (&mutex);
+void ClientLock () {
+  pthread_mutex_lock (&ClientMutex);
 }
 
-void SerialThreadUnlock () {
-    pthread_mutex_unlock (&mutex);
+void ClientUnlock () {
+  pthread_mutex_unlock (&ClientMutex);
 }
 
+/* mutex to lock Command / Multicommand operations */
+static pthread_mutex_t CommandMutex = PTHREAD_MUTEX_INITIALIZER;
+
+void CommandLock () {
+  //  fprintf (stderr, "command lock\n");
+  pthread_mutex_lock (&CommandMutex);
+}
+
+void CommandUnlock () {
+  //  fprintf (stderr, "command unlock\n");
+  pthread_mutex_unlock (&CommandMutex);
+}
+
+/* mutex to lock Control operations */
+static pthread_mutex_t ControlMutex = PTHREAD_MUTEX_INITIALIZER;
+
+void ControlLock (char *func) {
+  // fprintf (stderr, "control lock %s\n", func);
+  pthread_mutex_lock (&ControlMutex);
+}
+
+void ControlUnlock (char *func) {
+  // fprintf (stderr, "control unlock %s\n", func);
+  pthread_mutex_unlock (&ControlMutex);
+}
+
+/* mutex to lock Job / Task operations */
+static pthread_mutex_t JobTaskMutex = PTHREAD_MUTEX_INITIALIZER;
+
+void JobTaskLock () {
+  //  fprintf (stderr, "jobtask lock\n");
+  pthread_mutex_lock (&JobTaskMutex);
+}
+
+void JobTaskUnlock () {
+  //  fprintf (stderr, "jobtask unlock\n");
+  pthread_mutex_unlock (&JobTaskMutex);
+}
+
Index: anches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pcontrol/JobID.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pcontrol/JobID.c	(revision 23593)
+++ 	(revision )
@@ -1,64 +1,0 @@
-# include "pcontrol.h"
-
-static IDtype CurrentJobID  = 0;
-static IDtype CurrentHostID = 0;
-
-/* for now, no persistence : we could use the date/time to seed the upper byte(s) if needed */
-void InitIDs () {
-  CurrentJobID = 0;
-  CurrentHostID = 0;
-}
-
-IDtype NextJobID () {
-
-  IDtype ID;
-
-  ID = CurrentJobID;
-  CurrentJobID ++;
-  return (ID);
-}
-
-IDtype NextHostID () {
-
-  IDtype ID;
-
-  ID = CurrentHostID;
-  CurrentHostID ++;
-  return (ID);
-}
-
-void PrintID (gpDest dest, IDtype ID) {
-
-  unsigned short int word0, word1, word2, word3;
-
-  word0 = 0xffff & ID;
-  word1 = 0xffff & (ID >> 16);
-  word2 = 0xffff & (ID >> 32);
-  word3 = 0xffff & (ID >> 48);
-
-  gprint (dest, "%x.%x.%x.%x", word3, word2, word1, word0);
-}
-
-IDtype GetID (char *IDword) {
-
-  int Nargs;
-  IDtype ID;
-  unsigned short int word0, word1, word2, word3;
-
-  Nargs = sscanf (IDword, "%x.%x.%x.%x", &word0, &word1, &word2, &word3);
-  if (Nargs == 4) {
-    ID = 0;
-    ID |= (word0 << 0);
-    ID |= (word1 << 16);
-    ID |= (word2 << 32);
-    ID |= (word3 << 48);
-    return ID;
-  } 
-    
-  ID = strtoll (IDword, &endptr, 10);
-  if (*endptr == 0) {
-    return ID;
-  }
-
-  return 0;
-}
Index: /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pcontrol/JobOps.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pcontrol/JobOps.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pcontrol/JobOps.c	(revision 23594)
@@ -202,4 +202,10 @@
   ALLOCATE (job, Job, 1);
 
+  job[0].JobID    = NextJobID();
+  if (job[0].JobID < 0) {
+    free (job);
+    return -1;
+  }
+
   job[0].argc     = argc;
   job[0].argv     = argv;
@@ -220,6 +226,4 @@
   job[0].dtime = 0.0;
   job[0].pid = 0;
-
-  job[0].JobID    = NextJobID();
   job[0].host     = NULL;
 
Index: anches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pcontrol/QueueOps.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pcontrol/QueueOps.c	(revision 23593)
+++ 	(revision )
@@ -1,67 +1,0 @@
-# include "pcontrol.h"
-
-/* get object from point in stack (negative == distance from end) */
-void *GetStack (Stack *stack, int where) {
-
-  int i;
-  void *object;
-  
-  ASSERT (stack != NULL, "stack missing");
-
-  /* STACK_TOP == 0, STACK_BOTTOM == -1 */
-  /* this code correctly handles the negative 'where' and Nobject == 0 */
-  if (where < 0) where += stack[0].Nobject;
-  if (where < 0) return (NULL);
-  if (where >= stack[0].Nobject) return (NULL);
-
-  object = stack[0].object[where];
-  stack[0].Nobject --;
-  for (i = where; i < stack[0].Nobject; i++) {
-    stack[0].object[i] = stack[0].object[i+1];
-  }
-  return (object);
-}
-
-/* push object on top of stack */
-int PutStack (Stack *stack, int where, void *object) {
-
-  int i;
-
-  ASSERT (stack != NULL, "stack missing");
-
-  /* STACK_TOP == 0, STACK_BOTTOM == -1 */
-  /* this code correctly handles the negative 'where' and Nobject == 0 */
-  if (where < 0) where += stack[0].Nobject + 1;
-  if (where < 0) return (FALSE);
-  if (where > stack[0].Nobject) return (FALSE);
-
-  /* extend stack as needed */
-  if (stack[0].Nobject >= stack[0].NOBJECT) {
-    stack[0].NOBJECT += 100;
-    REALLOCATE (stack[0].object, void *, stack[0].NOBJECT);
-  }
-
-  for (i = stack[0].Nobject; i > where; i--) {
-    stack[0].object[i] = stack[0].object[i-1];
-  }
-  stack[0].object[where] = object;
-  stack[0].Nobject ++;
-  return (TRUE);
-}
-
-/* allocate stack, setup with default values, allocate data */
-Stack *InitStack () {
-
-  Stack *stack;
-
-  ALLOCATE (stack, Stack, 1);
-
-  stack[0].Nobject = 0;
-  stack[0].NOBJECT = 50;
-  ALLOCATE (stack[0].object, void *, stack[0].NOBJECT);
-  return (stack);
-}
-
-/* these stacks are not super efficient, and should probably be replaced with linked lists, 
-   but I find these easier to get my brain around
-*/
Index: anches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pcontrol/ResetJob.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pcontrol/ResetJob.c	(revision 23593)
+++ 	(revision )
@@ -1,61 +1,0 @@
-# include "pcontrol.h"
-
-// XXX deprecated
-
-int ResetJob (Job *job) {
-  
-  int       status;
-  Host     *host;
-
-  /** must have a valid host : if not, move to pending? **/
-  ASSERT (job != NULL, "job missing");
-
-  host = (Host *) job[0].host;
-  ASSERT (job != NULL, "host missing");
-
-  /* we have tried to reset the job; may not get status */
-  job[0].Reset = TRUE;
-
-  status = PclientCommand (host, "reset");
-
-  /* check on success of pclient command */
-  switch (status) {
-    case PCLIENT_DOWN:
-      if (VerboseMode()) gprint (GP_ERR, "host %s is down\n", host[0].hostname);
-      HarvestHost (host[0].pid);
-      PutHost (host, PCONTROL_HOST_DOWN, STACK_BOTTOM);
-      return (FALSE);
-
-    case PCLIENT_GOOD:
-      host[0].response_state = PCONTROL_RESP_RESET_JOB;
-      host[0].response = PCLIENT_PROMPT;
-      FlushIOBuffer (&host[0].comms_buffer, 0x100);
-      PutHost (host, PCONTROL_HOST_RESP, STACK_BOTTOM);
-      return (TRUE);
-
-    default:
-      ABORT ("unknown status for pclient command");  
-  }
-  ABORT ("should not reach here (ResetJob)"); 
-}
-
-int ResetJobResponse (Host *host) {
-  
-  int       status;
-  IOBuffer *buffer;
-
-  /* job must have assigned host */
-  ASSERT (host, "missing host");
-  ASSERT (host[0].job, "missing job");
-  buffer = host[0].comms_buffer;
-
-  gprint (GP_ERR, "message received (ResetJob)\n");  
-  return (TRUE);
-}
-
-/* if machine is down, return FALSE
-   this will place job back in BUSY state,
-   next check of job will catch state and
-   put machine down correctly
-
-*/
Index: /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pcontrol/job.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pcontrol/job.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pcontrol/job.c	(revision 23594)
@@ -11,4 +11,8 @@
       return (FALSE);
   }    
+
+  if (get_argument (argc, argv, "-h")) goto usage;
+  if (get_argument (argc, argv, "-help")) goto usage;
+  if (get_argument (argc, argv, "--help")) goto usage;
 
   Host = NULL;
@@ -36,8 +40,6 @@
 
   if (argc < 2) {
-    gprint (GP_ERR, "USAGE: job [options] (arg0) (arg1) ... (argN)\n");
-    gprint (GP_ERR, "  arguments of the form @MAX_THREADS@ will be replaced when the job is launched\n");
     FREE (Host);
-    return (FALSE);
+    goto usage;
   }
   
@@ -48,6 +50,13 @@
   }
 
+  // a JobID < 0 mean the job was not accepted
   JobID = AddJob (Host, Mode, Timeout, targc, targv);
   gprint (GP_LOG, "JobID: %d\n", (int) JobID);
   return (TRUE);
+
+ usage:
+    gprint (GP_ERR, "USAGE: job [options] (arg0) (arg1) ... (argN)\n");
+    gprint (GP_ERR, "  options: -host, +host, -timeout\n");
+    gprint (GP_ERR, "  arguments of the form @MAX_THREADS@ will be replaced when the job is launched\n");
+    return (FALSE);
 }
Index: anches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pcontrol/stop.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Ohana/src/opihi/pcontrol/stop.c	(revision 23593)
+++ 	(revision )
@@ -1,17 +1,0 @@
-# include "pcontrol.h"
-
-int stop (int argc, char **argv) {
-
-  if (argc != 1) {
-    gprint (GP_ERR, "USAGE: stop\n");
-    return (FALSE);
-  }
-
-# ifdef THREADED
-  SetRunSystem (FALSE);
-# else
-  rl_event_hook = NULL;
-# endif
-
-  return (TRUE);
-}
Index: /branches/cnb_branches/cnb_branch_20090301/Ohana/src/relastro/src/ImageOps.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Ohana/src/relastro/src/ImageOps.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/Ohana/src/relastro/src/ImageOps.c	(revision 23594)
@@ -297,4 +297,6 @@
       mask = TRUE;
     }
+    if (~finite(catalog[c].measure[m].dR) || ~finite(catalog[c].measure[m].dD)) mask = TRUE;
+
     raw[i].mask = mask;
 
Index: /branches/cnb_branches/cnb_branch_20090301/Ohana/src/relastro/src/UpdateObjects.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Ohana/src/relastro/src/UpdateObjects.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/Ohana/src/relastro/src/UpdateObjects.c	(revision 23594)
@@ -96,4 +96,7 @@
 
       for (k = 0; k < catalog[i].average[j].Nmeasure; k++, m++) {
+
+	//exclude measurements which have non-finite astrometry
+	if (~finite(catalog[i].measure[m].dR) || ~finite(catalog[i].measure[m].dD)) continue;
 
 	// exclude measurements by previous outlier detection
Index: /branches/cnb_branches/cnb_branch_20090301/Ohana/src/relastro/src/bcatalog.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/Ohana/src/relastro/src/bcatalog.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/Ohana/src/relastro/src/bcatalog.c	(revision 23594)
@@ -8,4 +8,5 @@
   int mask;
   PhotCode *code;
+  int skipFew = skipPhotCodeKeep = skipPhotCodeSkip = skipTime = skipPhotFlag = skipSigmaLim = skipImagSelect = 0;
 
   // XXX in the future, use catalog[0].Nsecfilt only?  allow catalogs to have variable Nsecfilt?
@@ -23,6 +24,9 @@
   /* exclude stars not in range or with too few measurements */
   for (i = 0; i < catalog[0].Naverage; i++) {
-    if (catalog[0].average[i].Nmeasure <= SRC_MEAS_TOOFEW) continue;
-
+    if (catalog[0].average[i].Nmeasure <= SRC_MEAS_TOOFEW) {
+      skipFew++;
+      continue;
+    }
+    
     /* start with all stars good */
     subcatalog[0].average[Naverage] = catalog[0].average[i];
@@ -52,5 +56,8 @@
           if (photcodesKeep[k][0].code == GetPhotcodeEquivCodebyCode(catalog[0].measure[offset].photcode)) found = TRUE;
         }
-        if (!found) continue;
+        if (!found) {
+	  skipPhotCodeKeep++;
+	  continue;
+	}
       }
       if (NphotcodesSkip > 0) {
@@ -60,17 +67,26 @@
           if (photcodesSkip[k][0].code == GetPhotcodeEquivCodebyCode(catalog[0].measure[offset].photcode)) found = TRUE;
         }
-        if (found) continue;
+        if (found) {
+	  skipPhotCodeSkip++;
+	  continue;
+	}
       }
 
       /* select measurements by time */
       if (TimeSelect) {
-        if (catalog[0].measure[offset].t < TSTART) continue;
-        if (catalog[0].measure[offset].t > TSTOP) continue;
+        if (catalog[0].measure[offset].t < TSTART) {
+	  skipTime++;
+	  continue;
+	}
+        if (catalog[0].measure[offset].t > TSTOP) {
+	  skipTime++;
+	  continue;
+	}
       }
-
+      
       /* select measurements by quality */
       // XXX FIX THIS!!
       // if (DophotSelect && (catalog[0].measure[offset].dophot != DophotValue)) continue;
-
+      
       /* select measurements by quality */
       if (PhotFlagSelect) {
@@ -81,17 +97,24 @@
           mask = code[0].astromBadMask;
         }
-        if (mask & catalog[0].measure[offset].photFlags) continue;
+        if (mask & catalog[0].measure[offset].photFlags) {
+	  skipPhotFlag++;
+	  continue;
+	}
       }
-
+      
       /* select measurements by measurement error */
-      if ((SIGMA_LIM > 0) && (catalog[0].measure[offset].dM > SIGMA_LIM)) continue;
-
+      if ((SIGMA_LIM > 0) && (catalog[0].measure[offset].dM > SIGMA_LIM)) {
+	skipSigmaLim++;
+	continue;
+      }
+      
       /* select measurements by mag limit */
       if (ImagSelect) {
         mag = PhotInst (&catalog[0].measure[offset]);
-        if (mag < ImagMin) continue;
-        if (mag > ImagMax) continue;
+        if (mag < ImagMin || mag > ImagMax) {
+	  skipImagSelect++;
+	  continue;
+	}
       }
-
       // re-assess on each run of relastro if a measurement should be used
 
@@ -139,4 +162,12 @@
 
   if (VERBOSE) {
+    fprintf(stderr, "Reasons for exclusion in bcatalog:\n");
+    fprintf(stderr, "\ntoo few: %d \nphotCodeSkip: %d \nphotCodeKeep: %d \ntime: %d\n",
+	    skipfew, skipPhotCodeKeep, skipPhotCodeSkip, skipTime);
+    fprintf(stderr, "photFlag: %d \nmagnitude error: %d \nImag: %d\n",
+	    skipPhotFlag, skipSigmaLim, skipImagSelect);
+  }
+
+  if (VERBOSE) {
     fprintf (stderr, "%d: using %d stars (%d measures) for catalog\n", i,
              subcatalog[0].Naverage, subcatalog[0].Nmeasure);
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 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/PS-IPP-Config/lib/PS/IPP/Config.pm	(revision 23594)
@@ -388,5 +388,5 @@
     }
 
-    return 1 if defined $self->{nebulous}; # Already started
+    return $self->{nebulous} if defined $self->{nebulous}; # Already started
 
     my $server = metadataLookupStr( $self->{_siteConfig}, 'NEB_SERVER' ); # Nebulous server
@@ -398,5 +398,5 @@
     my $neb = eval { Nebulous::Client->new( proxy => $server ); };
     if ($@ or not defined $neb) {
-        carp "Unable to find NEB_SERVER in camera configuration file.";
+        carp "Unable to create a Nebulous::Client object with proxy => $server";
         return undef;
     }
@@ -404,5 +404,17 @@
     $self->{nebulous} = $neb;
 
-    return 1;
+    return $neb;
+}
+
+sub nebulous
+{
+    my $self = shift;
+    carp "no parameters are allowed" if @_;
+
+    # check to see if we already have a live nebulous object
+    return $self->{nebulous} if defined $self->{nebulous};
+
+    # if not, call _neb_start() and return the new object
+    return $self->_neb_start() or ( carp "Can't start Nebulous" and return undef );
 }
 
@@ -421,6 +433,5 @@
 
         if ($scheme eq 'neb') {
-            $self->_neb_start() or ( carp "Can't start Nebulous" and return undef );
-            my $neb = $self->{nebulous}; # Nebulous handle
+            my $neb = $self->nebulous; # Nebulous handle
             if ($create_if_doesnt_exist) {
                 my $status = eval { $neb->stat( $name ); };
@@ -485,6 +496,5 @@
         $scheme = lc($scheme);
         if ($scheme eq 'neb') {
-            $self->_neb_start() or ( carp "Unable to start Nebulous" and return undef );
-            my $fh = eval { $self->{nebulous}->open_create( $name ); };
+            my $fh = eval { $self->nebulous->open_create( $name ); };
             if ($@ or not defined $fh) {
                 carp "Unable to open/create Nebulous handle $name";
@@ -524,6 +534,5 @@
         $scheme = lc($scheme);
         if ($scheme eq 'neb') {
-            $self->_neb_start() or ( carp "Unable to start Nebulous" and return undef );
-            my $fh = eval { $self->{nebulous}->open_create( $name ) };
+            my $fh = eval { $self->nebulous->open_create( $name ) };
             if ($@ or not defined $fh) {
                 carp "Unable to open/create Nebulous handle $name";
@@ -556,6 +565,5 @@
     my $scheme = file_scheme($name); # The scheme, e.g., file://, path://
     if (defined $scheme and lc($scheme) eq 'neb') {
-        $self->_neb_start() or ( carp "Unable to start Nebulous" and return undef );
-        $name = eval { $self->{nebulous}->create( $name ) };
+        $name = eval { $self->nebulous->create( $name ) };
         if ($@ or not defined $name) {
             carp "Unable to create Nebulous handle $name";
@@ -575,6 +583,5 @@
     my $scheme = file_scheme($name); # The scheme, e.g., file://, path://
     if (defined $scheme and lc($scheme) eq 'neb') {
-        $self->_neb_start() or ( carp "Unable to start Nebulous" and return undef );
-        my $found = eval { $self->{nebulous}->find_instances( $name ); };
+        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);
@@ -595,6 +602,5 @@
     my $scheme = file_scheme($target); # The scheme, e.g., file://, path://
     if (defined $scheme and lc($scheme) eq 'neb') {
-        $self->_neb_start() or ( carp "Unable to start Nebulous" and return undef );
-        $target = eval { $self->{nebulous}->create( $target ); };
+        $target = eval { $self->nebulous->create( $target ); };
         if ($@ or not defined $target) {
             carp "Unable to create Nebulous handle";
@@ -646,6 +652,5 @@
     my $scheme = file_scheme($name); # The scheme, e.g., file://, path://
     if (defined $scheme and lc($scheme) eq 'neb') {
-        $self->_neb_start() or ( carp "Unable to start Nebulous" and return undef );
-        $status = eval { $self->{nebulous}->delete( $name ); };
+        $status = eval { $self->nebulous->delete( $name ); };
         ( carp "Unable to delete Nebulous handle $name" and return undef ) if $@;
     } else {
Index: /branches/cnb_branches/cnb_branch_20090301/dbconfig/changes.txt
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/dbconfig/changes.txt	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/dbconfig/changes.txt	(revision 23594)
@@ -740,5 +740,5 @@
 ALTER TABLE diffSkyfile ADD COLUMN magicked TINYINT;
 
--- drop exiting foreign key constraints
+-- drop existing foreign key constraints
 ALTER TABLE diffInputSkyfile drop FOREIGN KEY diffInputSkyfile_ibfk_1;
 ALTER TABLE diffInputSkyfile drop FOREIGN KEY diffInputSkyfile_ibfk_2;
@@ -836,2 +836,133 @@
 show create table detResidImfile;
 alter table detResidImfile drop foreign key detResidImfile_ibfk_3;
+
+
+-- Version: 1.1.50  magic and distribution changes
+
+ALTER TABLE magicDSRun ADD COLUMN label VARCHAR(64) after cam_id;
+ALTER TABLE magicDSRun ADD KEY(label);
+
+-- replace null values for magicked flags with zero
+UPDATE rawImfile SET magicked = 0 WHERE magicked IS NULL;
+UPDATE chipProcessedImfile SET magicked = 0 WHERE magicked IS NULL;
+UPDATE warpSkyfile SET magicked = 0 WHERE magicked IS NULL;
+UPDATE diffSkyfile SET magicked = 0 WHERE magicked IS NULL;
+
+CREATE TABLE distRun (
+    dist_id     BIGINT AUTO_INCREMENT,
+    target_id   BIGINT,
+    stage       VARCHAR(64),
+    stage_id    BIGINT,
+    label       VARCHAR(64),
+    outroot     VARCHAR(255),
+    clean       TINYINT,
+    state       VARCHAR(64),
+    time_stamp  TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+    fault       SMALLINT,
+    PRIMARY KEY(dist_id),
+    KEY(state),
+    KEY(label),
+    FOREIGN KEY(target_id) REFERENCES distTarget(target_id)
+)  ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE distComponent (
+    dist_id     BIGINT, 
+    component   VARCHAR(64),
+    bytes       INT,
+    md5sum      VARCHAR(32),
+    state       VARCHAR(64),
+    name        VARCHAR(255),
+    fault       SMALLINT,
+    PRIMARY KEY(dist_id, component),
+    KEY(state),
+    FOREIGN KEY(dist_id) REFERENCES distRun(dist_id)
+)  ENGINE=innodb DEFAULT CHARSET=latin1;
+
+
+-- change state fields to match the rest of the system
+UPDATE magicRun SET state = 'new' WHERE state = 'run';
+UPDATE magicRun SET state = 'full' WHERE state = 'stop';
+UPDATE magicDSRun SET state = 'new' WHERE state = 'run';
+UPDATE magicDSRun SET state = 'full' WHERE state = 'stop';
+
+ALTER TABLE rawExp ADD COLUMN magicked TINYINT;
+ALTER TABLE chipRun ADD COLUMN magicked TINYINT;
+ALTER TABLE warpRun ADD COLUMN magicked TINYINT;
+ALTER TABLE diffRun ADD COLUMN magicked TINYINT;
+
+update rawExp  SET magicked = 0;
+update chipRun SET magicked = 0;
+update warpRun SET magicked = 0;
+update diffRun SET magicked = 0;
+
+-- The following only applies to the gpc1 database (and recent copies)
+update chipRun SET magicked = 1 where chip_id = 11955;
+
+CREATE TABLE distTarget (
+    target_id   BIGINT AUTO_INCREMENT,
+    obs_mode    VARCHAR(64),
+    stage       VARCHAR(64),
+    clean       TINYINT,
+    state       VARCHAR(64),
+    comment     VARCHAR(255),
+    PRIMARY KEY(target_id)
+)  ENGINE=innodb DEFAULT CHARSET=latin1;
+
+
+CREATE TABLE rcDSProduct (
+    prod_id     BIGINT AUTO_INCREMENT,
+    name        VARCHAR(64),
+    dbname      VARCHAR(64),
+    dbhost      VARCHAR(64),
+    prod_root   VARCHAR(255),
+    PRIMARY KEY(prod_id)
+)  ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE rcDestination (
+    dest_id     BIGINT AUTO_INCREMENT,
+    prod_id     BIGINT,
+    name        VARCHAR(64),
+    status_uri  VARCHAR(255),
+    comment     VARCHAR(255),
+    last_fileset VARCHAR(255),
+    state       VARCHAR(64),
+    PRIMARY KEY(dest_id),
+    FOREIGN KEY(prod_id) REFERENCES rcDSProduct(prod_id)
+)  ENGINE=innodb DEFAULT CHARSET=latin1;
+
+
+CREATE TABLE rcInterest (
+    int_id      BIGINT AUTO_INCREMENT,
+    dest_id     BIGINT,
+    target_id   BIGINT,
+    prod_id     BIGINT,
+    state       VARCHAR(64),
+    PRIMARY KEY(int_id),
+    FOREIGN KEY(dest_id) REFERENCES rcDestination(dest_id),
+    FOREIGN KEY(target_id) REFERENCES distTarget(target_id),
+    FOREIGN KEY(prod_id) REFERENCES rcDSProduct(prod_id)
+)  ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE rcDSFileset (
+    fs_id       BIGINT AUTO_INCREMENT,
+    dist_id     BIGINT,
+    prod_id     BIGINT,
+    name        VARCHAR(64),
+    state       VARCHAR(64),
+    registered  TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+    PRIMARY KEY(fs_id),
+    FOREIGN KEY(dist_id) REFERENCES distRun(dist_id),
+    FOREIGN KEY(prod_id) REFERENCES rcDSProduct(prod_id)
+)  ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE rcRun (
+    rc_id       BIGINT AUTO_INCREMENT,
+    fs_id       BIGINT,
+    dest_id     BIGINT,
+    state       VARCHAR(64),
+    registered  TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+    PRIMARY KEY(rc_id),
+    FOREIGN KEY(fs_id) REFERENCES rcDSFileset(fs_id),
+    FOREIGN KEY(dest_id) REFERENCES rcDestination(dest_id)
+)  ENGINE=innodb DEFAULT CHARSET=latin1;
+
Index: /branches/cnb_branches/cnb_branch_20090301/dbconfig/chip.md
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/dbconfig/chip.md	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/dbconfig/chip.md	(revision 23594)
@@ -11,4 +11,5 @@
     tess_id     STR         64
     end_stage   STR         64      # Key
+    magicked	BOOL        f
 END
 
Index: /branches/cnb_branches/cnb_branch_20090301/dbconfig/config.md
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/dbconfig/config.md	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/dbconfig/config.md	(revision 23594)
@@ -2,4 +2,4 @@
     pkg_name        STR     ippdb
     pkg_namespace   STR     ippdb
-    pkg_version     STR     1.1.49
+    pkg_version     STR     1.1.50
 END
Index: /branches/cnb_branches/cnb_branch_20090301/dbconfig/diff.md
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/dbconfig/diff.md	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/dbconfig/diff.md	(revision 23594)
@@ -11,4 +11,5 @@
     tess_id     STR         64      # Key
     exp_id      S64         0       # fkey(exp_id) ref rawExp(exp_id)
+    magicked	BOOL        f
 END
 
Index: /branches/cnb_branches/cnb_branch_20090301/dbconfig/dist.md
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/dbconfig/dist.md	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/dbconfig/dist.md	(revision 23594)
@@ -3,9 +3,9 @@
     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
+    time_stamp  UTC         0001-01-01T00:00:00Z
     fault       S16         0
 END
@@ -21,2 +21,10 @@
 END
 
+distTarget METADATA
+    target_id   S64        0       # Primary Key
+    obs_mode    STR        64
+    clean       BOOL       f
+    state       STR        64
+    comment     STR        255
+END
+
Index: /branches/cnb_branches/cnb_branch_20090301/dbconfig/ipp.m4
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/dbconfig/ipp.m4	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/dbconfig/ipp.m4	(revision 23594)
@@ -22,2 +22,3 @@
 include(flatcorr.md)
 include(pstamp.md)
+include(dist.md)
Index: /branches/cnb_branches/cnb_branch_20090301/dbconfig/magic.md
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/dbconfig/magic.md	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/dbconfig/magic.md	(revision 23594)
@@ -42,5 +42,5 @@
 
 magicDSRun METADATA
-    magic_ds_id S64         0       # Primary Key fkey(magic_ds_id)
+    magic_ds_id S64         0       # Primary Key
     magic_id    S64         0       # Primary Key fkey(magic_id) ref magicRun(magic_id)
     state       STR         0       # Key
@@ -48,4 +48,5 @@
     stage_id    S64         0
     cam_id      S64         0
+    label       STR         64      # key
     outroot     STR         255
     recoveryroot    STR     255
@@ -55,5 +56,5 @@
 
 magicDSFile METADATA
-    magic_ds_id S64         0       # Primary Key
+    magic_ds_id S64         0       # Primary Key fkey(magic_ds_id) ref magicDSRun(magic_ds_id)
     component   STR         64
     backup_path_base  STR         255
Index: /branches/cnb_branches/cnb_branch_20090301/dbconfig/tasks.md
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/dbconfig/tasks.md	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/dbconfig/tasks.md	(revision 23594)
@@ -171,4 +171,5 @@
     fault       S16         0       # Key NOT NULL
     epoch       UTC         0001-01-01T00:00:00Z
+    magicked	BOOL        f
 END
 
@@ -235,5 +236,5 @@
     moon_alt    F32         0.0
     moon_phase  F32         0.0
-    ignored	BOOL        FALSE
+    ignored	BOOL        f
     hostname    STR         64
     fault       S16         0       # Key NOT NULL
Index: /branches/cnb_branches/cnb_branch_20090301/dbconfig/warp.md
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/dbconfig/warp.md	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/dbconfig/warp.md	(revision 23594)
@@ -21,5 +21,5 @@
     end_stage   STR         64      # Key
     registered  TAI         NULL
-# if magic is T then look for the exp_id in the magic output tables
+    magicked	BOOL        f
 END
 
Index: /branches/cnb_branches/cnb_branch_20090301/extsrc/Makefile
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/extsrc/Makefile	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/extsrc/Makefile	(revision 23594)
@@ -0,0 +1,31 @@
+
+PACKAGES = gpcsw
+
+default: all
+
+all:
+	for i in $(PACKAGES); do make $$i || exit; done
+
+install:
+	for i in $(PACKAGES); do make $$i.install || exit; done
+
+clean:
+	for i in $(PACKAGES); do make $$i.clean || exit; done
+
+update:
+	for i in $(PACKAGES); do make $$i.update || exit; done
+
+$(PACKAGES):
+	if [ -d "$@" ]; then (cd $@ && make); fi
+
+%.install:
+	if [ -d "$*" ]; then make $*; fi
+	if [ -d "$*" ]; then (cd $* && make install); fi
+
+%.clean:
+	if [ -d "$*" ]; then (cd $* && make clean); fi
+
+%.update:
+	if [ -d "$*" ]; then (cd $* && make update); fi
+
+
Index: /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/Make.Common.fixed
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/Make.Common.fixed	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/Make.Common.fixed	(revision 23594)
@@ -0,0 +1,726 @@
+#
+# $Id: Make.Common,v 2.17 2004/09/05 10:10:45 thomas Exp $
+# $Locker:  $
+#
+# 2006-2-25: Windows cross-compilation support (not in CFHT RCS.)
+# 2006-4-19: "PREFIX" patch added by Sidik (not in CFHT RCS.)
+#
+# Make.Common - include at top of Makefiles for some common defaults.
+# Override any variables by setting them after including this file.
+# Then include Make.Common a second time, followed by any project
+# specific targets. For example, a typical library Makefile looks like this:
+#
+#   # Makefile for libfoo
+#   include ../Make.Common
+#   SRCS=foo1.c foo2.c foo3.c
+#   HDRS=foo.h
+#   include ../Make.Common
+#
+# If SRCS just equals *.c and *.cc (recommended) then you can
+# omit it completely from your makefile.  When a new C file
+# appears in the directory for libfoo, it will then automatically
+# be added to libfoo on the next build.  HDRS gives the header
+# files you want to install.  If you do not have any internal .h
+# files in your library, you can omit HDRS as well and *.h is assumed.
+# For a program, a typical Makefile looks like this:
+#
+#   # Makefile for foo
+#   include ../Make.Common
+#   VERSION=1.1
+#   $(EXECNAME): $(OBJS) libfoo.a libcfht.a
+#   include ../Make.Common
+#
+# Again, if you don't want it to pick up *.c and *.cc, then you might
+# want to define SRCS explicitly (OBJS will be generated for you).
+# If you omit VERSION, the current date (YYMMDD) is used instead.
+# And for a project toplevel directory, a Makefile might look like:
+#
+#   # Makefile for foo project directory
+#   include ../Make.Common
+#   SUBDIRS=libfoo foo
+#   foo.d: libfoo.d.install
+#   include ../Make.Common
+#
+# Use this last template whenever creating a subdirectory off of
+# the main directory which contains Make.Common.  Create a symlink
+# in the new directory so that the "leaves" (programs and libraries)
+# can always include "../Make.Common" from their Makefiles.
+#
+# The dependency line "foo.d: libfoo.d.install" indicates that libfoo
+# must be _installed_ before foo is _built_.  Other dependencies
+# within a program are generated automatically by "make dep".  It
+# only generates dependencies for files included with "" (quotes)
+# and not <>, so if want your program to rebuild if cfht.h changes,
+# include it with "cfht/cfht.h" instead of <cfht/cfht.h>.
+#
+# More user information can be found at http://software/make.html
+# The rest of the comments in this file mostly pertain to hacking
+# on Make.Common itself.
+#
+# ---------------------------------------------------------------------------
+ifndef HAVE_VARS
+#
+# On the first pass, define defaults for makefile variables.
+# Then the user gets a chance to override any, and finally
+# Make.Common is included again.
+#
+HAVE_VARS := True
+#
+# Default is to build everything, but not install it (except that
+# some libraries may get installed if other projects depend on them).
+#
+default: all
+#
+#                          --- Directories ---
+#
+# Find out where to store the architecture dependent objects.
+# Apr 2000: Added some temporary transitional hacks to help migration.
+#           Remove /tmp_mnt, /local, /usr/local/cfht to /cfht, /cfht/dev -> /cfht/src
+#           This might break building at other sites!
+# 
+FIND_ROOT := $(shell DA=`pwd`; DR="obs"; \
+while [ ! -f $$DA/ThisIsTopLevel -a "$$DA" != "/" ]; do \
+  DA=`cd $$DA/..;pwd | sed -e 's,^/local/,/,' -e 's,/tmp_mnt/,/,' -e 's,^/usr/local/cfht/,/cfht/,' -e 's,/cfht/dev,/cfht/src,'`; DR=../$$DR; \
+done; \
+if [ "$$DA" = "/" ]; then \
+echo "ERROR: You must create an empty file called \`ThisIsTopLevel' in a common" 1>&2;\
+echo "  directory shared by project sources.  e.g.: /usr/local/src/ThisIsTopLevel" 1>&2;\
+echo "  The directory one level higher will be used as a base for lib,bin,include" 1>&2;\
+echo "  e.g. /usr/local/{bin,lib,...} if you create /usr/local/src/ThisIsTopLevel." 1>&2;\
+echo "*** Please press ^C now and create this file before continuing..." 1>&2;\
+read Dmy; else \
+echo `cd $$DA/..;pwd | sed -e 's,^/local/,/,' -e 's,/tmp_mnt/,/,' -e 's,^/usr/local/cfht/,/cfht/,' -e 's,/cfht/dev,/cfht/src,'` ../$$DR $$DA; fi)
+
+#
+# This defines that basic paths that are commonly used during the build
+# process.  DIR_GNU must be the prefix where you've installed gcc,
+# gmake, and ginstall.  The rest are all install locations for header
+# files, man pages, libraries, objects, and programs.
+#
+DIR_GNU   := /apps/gnu
+PREFIX    := $(word 1,$(FIND_ROOT))
+DIR_OBSREL:= $(word 2,$(FIND_ROOT))
+DIR_SRC   := $(word 3,$(FIND_ROOT))
+#
+# Allow local settings to override PREFIX.  Make.Local is
+# included again at the very end, allowing any other
+# variables to be overridden.
+#
+ifeq ($(DIR_SRC)/Make.Local,$(wildcard $(DIR_SRC)/Make.Local))
+include $(DIR_SRC)/Make.Local
+endif
+#
+DIR_ROOT  := $(PREFIX)
+DIR_CONF  := $(DIR_ROOT)/conf
+DIR_BIN   := $(DIR_ROOT)/bin
+DIR_LIB   := $(DIR_ROOT)/lib
+DIR_MAN   := $(DIR_ROOT)/man
+DIR_INC   := $(DIR_ROOT)/include
+DIR_OBS   := $(DIR_ROOT)/obs
+#
+#                         --- Build Tools ---
+#
+# MAKEFILE may be useful during migration (if a project needs two styles
+# of makefiles at once.)  PATH should have at least DIR_GNU and DIR_BIN.
+# Rest are just the basic programs used to build the program.  User is
+# expected to run GNU Make 3.74 or better.  After that, everything here
+# should take care of making sure the right versions of things are run.
+#
+MAKEFILE = Makefile
+PATH     := .:$(DIR_GNU)/bin:$(DIR_BIN):/usr/local/bin:/usr/5bin:/usr/bin:/bin:/usr/ucb:$(PATH)
+SHELL	 = /bin/sh
+INSTALL  = ginstall
+INSTSTRIP= $(INSTALL) -s
+TAR	 = gtar
+AR	 = ar
+FC       = gfortran
+CC	 = gcc
+LD       = gcc
+CXX	 = g++
+CMM	 = g++	# for make depend
+PURIFY   = purify
+#
+#                       --- Build Tool Flags ---
+#
+# Standard flags to ar and gcc during compile/link phases.  The GNU compiler
+# allows -g and -O at the same time.  At installation, the executables are
+# stripped and become the equivalent of just a "-O" compiled version.  The
+# copy with the symbols stays in $(DIR_OBS) until the next make or make clean.
+#
+# The setting of CCWARN here gives messages comparable to most things that
+# lint used to check for.  Override in the project makefile by setting it
+# to nothing if it's too noisy.
+#
+# The flags in CCSHARE apply to two different things: -fPIC is used during
+# compilation and -shared applies to the link stage.
+#
+FCDEBUG  = -g -O -fno-automatic
+FFWARN   = -Wall
+FFLAGS   = $(FCDEBUG) $(EXTRA_FFLAGS) $(FFWARN) $(CCDEFS) $(CCINCS)
+CCDEBUG  = -g -O
+CCSHARE  = -fPIC -shared
+CCWARN	 = -Wall -Wstrict-prototypes # -Wshadow
+CFLAGS   = $(CCDEBUG) $(EXTRA_CFLAGS) $(CCWARN) $(CCDEFS) $(CCINCS) $(CCHACKS)
+LDFLAGS  = $(CCDEBUG) $(EXTRA_CFLAGS) $(CCLIBS)
+CXXFLAGS = $(subst -Wstrict-prototypes,,$(CFLAGS))
+ARFLAGS  = -rc
+#
+# Find out the host name and type that we are currently running on.
+# TARGET=OSname-OSmajor. No support is provided for cross-compilers.
+# Later in this file TARGET is looked at and is used to define
+# one of: -DHPUX -DCYGWIN32 -DLINUX -DSOLARIS or -DSUNOS.  If absolutely
+# necessary, use this for conditional compiles in your code.  If
+# further distinctions are needed for other OSes or OS versions,
+# make up a new define and add it to the section that checks TARGET.
+#
+HOSTNAME := $(strip $(shell hostname))
+ifeq ($(TARGET),)
+TARGET := $(shell echo `uname -s`-`uname -r | cut -d . -f 1`)
+endif
+DOMAIN := $(shell domainname 2> /dev/null || echo "unknown")
+#
+# At CFHT, make sure stuff gets installed as group `daprog':
+#
+ifeq ($(DOMAIN),cfht)
+INSTALL += -g daprog
+endif
+#
+# Override or += these variables in the project's Makefile
+#
+VERSION_DATE := $(shell date +%y%m%d | sed -e 's/:/0/')
+VERSION      = $(VERSION_DATE)
+EXTRA_CFLAGS =
+#
+# Include the following defines by adding CCDEFS+=$(VERSIONDEFS) to Makefile:
+#
+VERSIONDEFS=-DVERSION=$(VERSION) -DSOURCEDATE="\"`sourcedate . 2> /dev/null`\"" -DBUILDDATE="\"`date +'%b %d %Y'`\""
+#
+# Provide a HINT for programs that can use either syslog or cfht_log
+#
+ifeq ($(origin NO_CFHTLOG), undefined)
+NO_CFHTLOG   := $(shell if [ ! -p /tmp/pipes/syslog.np ];then echo "-DNO_CFHTLOG";fi)
+endif
+#
+# Don't build in support for EPICS except on saturn and neptune (RPM & hform)
+#
+ifeq ($(origin USE_EPICS), undefined)
+USE_EPICS    := $(shell if [ "$(HOSTNAME)" = "saturn" -o "$(HOSTNAME)" = "neptune" ]; then echo "-DUSE_EPICS";fi)
+endif
+#
+CCDEFS       = $(NO_CFHTLOG) $(USE_EPICS)
+CCINCS       = -I. -Iinclude -I$(DIR_INC)
+CCLIBS       = -L$(DIR_LIB)
+CCHACKS      =
+#
+#                        --- Program Files ---
+#
+# The *INST variables end up containing the list of targets as their
+# installed names with the full paths like $(DIR_INC) and $(DIR_CONF)
+# tacked on to each filename.  NEEDTITLES is a list of files which
+# Sidik's titler program should attempt to place a copyright header in.
+#
+SRCS	     = $(shell ls *.f *.f77 *.c *.cc 2> /dev/null)
+HDRS	     = $(shell ls *.h *.hh 2> /dev/null)
+HDRINST	     = $(HDRS:%=$(DIR_INC)/$(PROJBASE)/%)
+MAN1         = $(shell cd ./man 2>/dev/null && /bin/ls *.1 2>/dev/null)
+MAN2         = $(shell cd ./man 2>/dev/null && /bin/ls *.2 2>/dev/null)
+MAN3         = $(shell cd ./man 2>/dev/null && /bin/ls *.3 2>/dev/null)
+MAN4         = $(shell cd ./man 2>/dev/null && /bin/ls *.4 2>/dev/null)
+MAN5         = $(shell cd ./man 2>/dev/null && /bin/ls *.5 2>/dev/null)
+MAN6         = $(shell cd ./man 2>/dev/null && /bin/ls *.6 2>/dev/null)
+MAN7         = $(shell cd ./man 2>/dev/null && /bin/ls *.7 2>/dev/null)
+MAN8         = $(shell cd ./man 2>/dev/null && /bin/ls *.8 2>/dev/null)
+CONFS        = $(shell cd ./conf 2>/dev/null && /bin/ls *.def *.par *.bm *.xbm *.rdb *.xrdb 2>/dev/null)
+SCRIPTS      = $(shell cd ./scripts 2>/dev/null && /bin/ls *.sh 2>/dev/null)
+COPYINST     = $(CONFS:%=$(DIR_CONF)/%) $(SCRIPTS:%.sh=$(DIR_BIN)/%) \
+	$(MAN1:%=$(DIR_MAN)/man1/%) \
+	$(MAN2:%=$(DIR_MAN)/man2/%) \
+	$(MAN3:%=$(DIR_MAN)/man3/%) \
+	$(MAN4:%=$(DIR_MAN)/man4/%) \
+	$(MAN5:%=$(DIR_MAN)/man5/%) \
+	$(MAN6:%=$(DIR_MAN)/man6/%) \
+	$(MAN7:%=$(DIR_MAN)/man7/%) \
+	$(MAN8:%=$(DIR_MAN)/man8/%)
+NEEDTITLES   = $(shell ls * | egrep -v "^\#|~$$" 2> /dev/null)
+#
+# SUBDIRS gets overridden only by Makefiles that just build other
+# subdirectories in their tree.  These directory nodes cannot generate
+# a program or a library (this is only done at the `leaves')
+#
+SUBDIRS      =
+#
+#                       --- System Dependent ---
+#
+# Some typical places we might find X11, typical extension for shared lib,
+# and extension for executables.
+#
+CCINCSX11 = -I/usr/X11/include
+CCLIBSX11 = -L/usr/X11/lib
+CCLINKX11 = -lX11 -lXext
+OBJ_LINK = obj
+EXE =
+SL  = .so
+#
+# Some overrides for a Win32 system running a cygnus win32 gcc
+#
+ifeq ($(TARGET), CYGWIN32_NT-4)
+CCDEFS   += -DCYGWIN32
+EXE       = .exe
+SL        = .dll
+endif
+#
+# Some overrides for HPUX (X11 in non-standard place, .sl for shared libs)
+#
+ifeq ($(TARGET), HP-UX-A)
+CCINCSX11 = -I/usr/include/X11R5
+CCLIBSX11 = -L/usr/lib/X11R5
+CCLINKX11 = -lX11
+CCDEFS   += -DHPUX -DHACK_XLIBS -DHACK_SELECT
+# X Libraries may have bugs, so X clients (hform) that really care
+# about this should add /usr/local/lib/wm (a CFHT-ism) to SHLIB_PATH.
+# select() prototype is broken, and missing_protos.h should fix it.
+CCHACKS   = -fwritable-strings
+SL        = .sl
+endif
+#
+# HP-UX 10.20 no longer has the scanf bug in the C library, so
+# doesn't need -fwritable-strings anymore.  X is also up to R6 now.
+#
+ifeq ($(TARGET), HP-UX-B)
+CCINCSX11 = -I/usr/include/X11R6
+CCLIBSX11 = -L/usr/lib/X11R6
+CCLINKX11 = -lX11
+CCDEFS   += -DHPUX
+SL        = .sl
+endif
+#
+# Linux often has dns in a separate libresolv and crypt in libcrypt.
+# Some installations require them to be explicitly linked.
+# Linux's utilities (tar, install) are the GNU versions, so no need
+# for the `g' prefix which we use on other platforms to ensure that
+# we're getting the GNU versions.
+#
+ifeq ($(TARGET), Linux-2)
+TAR = tar
+INSTALL = install
+CCINCSX11 = -I/usr/X11R6/include
+CCLIBSX11 = -L/usr/X11R6/lib
+CCLINKX11 = -lX11
+CCLINK  = -Wl,-R,$(DIR_LIB)
+ifeq (/usr/lib/libresolv.so,$(wildcard /usr/lib/libresolv.so))
+CCLINKNET += -lresolv
+endif
+ifeq (/usr/lib/libcrypt.so,$(wildcard /usr/lib/libcrypt.so))
+CCLINKNET += -lcrypt
+endif
+CCDEFS   += -DLINUX
+endif
+#
+# Solaris needs to link with libsocket and libnsl.  Also X11 is with openwin.
+#
+ifeq ($(TARGET), SunOS-5)
+CCINCSX11 = -I/usr/openwin/include
+CCLIBSX11 = -L/usr/openwin/lib
+CCLINKNET = -lsocket -lnsl
+# Without this the Solaris dynamic linker may not be able to find shared
+# versions of libg++ and libstdc++.  SunOS builds it from the -L's.
+CCLINK  = -Wl,-R,$(DIR_GNU)/lib
+CCDEFS   += -DSOLARIS
+endif
+#
+# Old suns are missing EXIT_FAILURE and EXIT_SUCCESS from stdlib.h
+#
+ifeq ($(TARGET), SunOS-4)
+CCDEFS   += -DSUNOS -D__USE_FIXED_PROTOTYPES__ -DEXIT_FAILURE=1 -DEXIT_SUCCESS=0
+endif
+#
+# Cross-compiler for VxWorks on a Sparc
+#
+ifeq ($(TARGET), VXSPARC)
+AR      = vxarsparc
+CC      = vxgccsparc
+LD      = vxgccsparc
+CXX     = vxg++sparc
+CMM	= vxg++sparc	# for make depend
+CROSSCC = vxsparc
+OBJ_LINK     = obj.vxsparc
+CCDEFS += -DCPU=SPARC -DVXWORKS -Dmain=$(PROJECT)
+DIR_BIN   := $(DIR_BIN)/$(CROSSCC)
+DIR_LIB   := $(DIR_LIB)/$(CROSSCC)
+DIR_OBS   := $(DIR_OBS)/$(CROSSCC)
+DIR_OBSREL:= $(DIR_OBSREL)/$(CROSSCC)
+NO_CFHTLOG:= -DNO_CFHTLOG
+INSTSTRIP= $(INSTALL)
+endif
+#
+# Cross-compiler for VxWorks on a PowerPC
+#
+ifeq ($(TARGET), VXPOWERPC)
+AR      = vxarpowerpc
+CC      = vxgccpowerpc
+LD      = vxgccpowerpc
+CXX     = vxg++powerpc
+CMM	= vxgccpowerpc	# for make depend, change to ++ if vxg++powerpc works
+CROSSCC = vxpowerpc
+OBJ_LINK     = obj.vxpowerpc
+CCDEFS += -DCPU=PPC604 -DVXWORKS -Dmain=$(PROJECT)
+DIR_BIN   := $(DIR_BIN)/$(CROSSCC)
+DIR_LIB   := $(DIR_LIB)/$(CROSSCC)
+DIR_OBS   := $(DIR_OBS)/$(CROSSCC)
+DIR_OBSREL:= $(DIR_OBSREL)/$(CROSSCC)
+NO_CFHTLOG:= -DNO_CFHTLOG
+INSTSTRIP= $(INSTALL)
+endif
+#
+# Cross-compiler for PowerPC405 (embedded)
+#
+ifeq ($(TARGET), PPC405)
+AR      = /apps/ppc405/powerpc-405-linux-gnu/gcc-3.4.2-glibc-2.2.5/bin/powerpc-405-linux-gnu-ar
+CC      = /apps/ppc405/powerpc-405-linux-gnu/gcc-3.4.2-glibc-2.2.5/bin/powerpc-405-linux-gnu-gcc
+LD      = /apps/ppc405/powerpc-405-linux-gnu/gcc-3.4.2-glibc-2.2.5/bin/powerpc-405-linux-gnu-gcc
+CXX     = /apps/ppc405/powerpc-405-linux-gnu/gcc-3.4.2-glibc-2.2.5/bin/powerpc-405-linux-gnu-g++
+CMM	= /apps/ppc405/powerpc-405-linux-gnu/gcc-3.4.2-glibc-2.2.5/bin/powerpc-405-linux-gnu-g++
+CROSSCC = ppc405
+OBJ_LINK     = obj.ppc405
+CCDEFS += -DPPC405
+DIR_BIN   := $(DIR_BIN)/$(CROSSCC)
+DIR_LIB   := $(DIR_LIB)/$(CROSSCC)
+DIR_OBS   := $(DIR_OBS)/$(CROSSCC)
+DIR_OBSREL:= $(DIR_OBSREL)/$(CROSSCC)
+NO_CFHTLOG:= -DNO_CFHTLOG
+INSTSTRIP= $(INSTALL)
+endif
+#
+# Cross-compiler for Windows i386
+#
+ifeq ($(TARGET), MINGW32)
+PATH   := /usr/local/cross-tools/bin:$(PATH)
+AR      = i386-mingw32msvc-ar
+CC      = i386-mingw32msvc-gcc
+LD      = i386-mingw32msvc-gcc
+CXX     = i386-mingw32msvc-g++
+CMM     = i386-mingw32msvc-g++
+CROSSCC = mingw32
+OBJ_LINK     = obj.mingw32
+EXE       = .exe
+SL        = .dll
+CCDEFS += -DMINGW32
+#DIR_BIN   := $(DIR_BIN)/$(CROSSCC)
+DIR_LIB   := $(DIR_LIB)/$(CROSSCC)
+DIR_OBS   := $(DIR_OBS)/$(CROSSCC)
+DIR_OBSREL:= $(DIR_OBSREL)/$(CROSSCC)
+NO_CFHTLOG:= -DNO_CFHTLOG
+INSTSTRIP= $(INSTALL)
+endif
+#
+#                  --- Project and Target Names ---
+#
+OBJ	  = $(DIR_OBS)/$(PROJECT)
+#
+# OBJS1 is just a temporary variable with *.cc converted to *.o and
+# OBJS contains *.cc + *.c -> *.o.  OBJS_PIC lists the names that
+# would be used if a shared library is being build (*.pic.o, in the
+# same directory where the other *.o files went.
+#
+OBJS3    = $(SRCS:%.cc=$(OBJ)/%.o)
+OBJS2	 = $(OBJS3:%.c=$(OBJ)/%.o)
+OBJS1    = $(OBJS2:%.f=$(OBJ)/%.o)
+OBJS     = $(OBJS1:%.f77=$(OBJ)/%.o)
+OBJS_PIC = $(OBJS:%.o=%.pic.o)
+#
+# If a program or library is generated, the subdirectory MUST have the
+# same name as the program it generates.  This is placed in PROJECT.
+# PROJBASE contains the same thing, unless it started with `lib', in
+# which case the lib is trimmed off.  This is how we know if we are
+# building a library or not.
+#
+PROJECT   = $(patsubst %-$(VERSION),%,$(notdir $(shell pwd)))
+PROJBASE  = $(PROJECT:lib%=%)
+#
+# `make all' causes the program to be deposited in this temporary directory
+#
+EXECNAME  = $(OBJ)/$(PROJBASE)$(EXE)
+#
+# `make install' strips it and copies it to the main bin/ directory.
+#
+EXECINST  = $(DIR_BIN)/$(PROJBASE)$(EXE)
+#
+# `make all' for libraries first assembles the .a here
+#
+LIBNAME   = $(OBJ)/$(PROJECT).a
+#
+# `make install' for libraries copies it unstripped to the lib/ directory
+#
+LIBINST   = $(DIR_LIB)/$(PROJECT).a
+#
+# Same all over again, except for the shared versions of the libraries.
+#
+SHLIBNAME = $(OBJ)/$(PROJECT)$(SL)
+SHLIBINST = $(DIR_LIB)/$(PROJECT)$(SL)
+#
+# This is where we detect if this is a library or a program
+#
+ifeq ($(PROJECT), $(PROJBASE))
+ALL	= $(EXECNAME)
+ALLINST = $(COPYINST) $(EXECINST)
+HDRINST =
+else
+ALL	= $(LIBNAME)
+# ALL += $(SHLIBNAME)
+ALLINST = $(HDRINST) $(LIBINST)
+# ALLINS += $(SHLIBINST)
+endif
+#
+# ------------------------- End of Variables -----------------------------
+else
+# -------------------------- Start of Rules ------------------------------
+#
+# This section gets read during the second pass.  It defines the standard
+# rules defined at http://software/make.html, using the variables defined
+# above (possibly with some overrides that the user put in between.)
+#
+.SUFFIXES: ; # Do not use default targets like '.c.o' and '.cc.o'
+#
+# Tell make never to expect a file by any of these names:
+#
+.PHONY:	clean all dep default checkdep checkbin preinstall libinstall install execlist execlist-sh
+#
+vpath %.h     $(DIR_INC)/cfht:$(DIR_INC)	# include files
+vpath %.hh    $(DIR_INC)			# C++ include files
+vpath %.bm    $(DIR_INC)			# bitmap include files
+vpath %.a     $(DIR_LIB)			# libraries
+vpath %$(SL)  $(DIR_LIB)			# shared libraries
+#
+#                        --- Rule Patterns ---
+#
+# These are just the standard ways to cause gcc to generate a .o from a .c,
+# g++ to generate a .o from a .cc, and to create libraries with ar (or
+# gcc in the case of shared libraries... which we are not really using yet.)
+#
+$(OBJ)/%.o: %.f          ; $(FC)  $(FFLAGS)   -c $*.f  -o $@
+$(OBJ)/%.o: %.c          ; $(CC)  $(CFLAGS)   -c $*.c  -o $@
+$(OBJ)/%.o: %.cc         ; $(CXX) $(CXXFLAGS) -c $*.cc -o $@
+$(OBJ)/%.pic.o: %.f      ; $(CC)  $(CFLAGS)   $(CCSHARE) -c $*.f  -o $@
+$(OBJ)/%.pic.o: %.c      ; $(CC)  $(CFLAGS)   $(CCSHARE) -c $*.c  -o $@
+$(OBJ)/%.pic.o: %.cc     ; $(CXX) $(CXXFLAGS) $(CCSHARE) -c $*.cc -o $@
+$(LIBNAME):  $(OBJS)      ; rm -f $@ ; $(AR) $(ARFLAGS) $@~ $^ ; mv $@~ $@
+$(SHLIBNAME): $(OBJS_PIC) ; rm -f $@ ; $(LD) $(LDFLAGS) $(CCSHARE) $^ $(CCLINK) -o $@
+#
+# The cryptic substitution that starts with $(^... takes all the dependencies
+# that this executable has and replaces anything like /usr/local/lib/libcfht.a
+# with a -lcfht.  WARNING: if a shared version exists, it will be used!
+#
+$(EXECNAME):
+	$(LD) $(LDFLAGS) $(^:$(DIR_LIB)/lib%.a=-l%) $(CCLINK) -o $@~
+	@mv $@~ $@
+#
+# NOTE: Use `make obj/myprogram-pure' to invoke this.  Also be sure you have
+#       $(EXECNAME) $(EXECNAME)-pure: in your project Makefile
+#
+$(EXECNAME)-pure: ; $(PURIFY) $(LD) $(LDFLAGS) $^ $(CCLINK) -o $@
+#
+# Sidik's titler program will insert comment header blocks into all your
+# source files if you create the proper Index files.
+#
+titles: ; @titler $(NEEDTITLES)
+#
+# `make execlist' at any level produces a list of all the "executables"
+# (scripts or compiled C programs) from the current level down.  It is
+# used by the version programs for pegasus accounts.
+#
+execlist: execlist-sh
+execlist-sh:
+	@for i in "" $(SCRIPTS:%.sh=%); do if [ "$$i" != "" ]; then echo $$i; fi; done
+
+ifneq ($(SUBDIRS),)
+#
+#         --- Standard Targets for project parent directories ---
+#                                    (See http://software/make.html)
+world: all install
+install:	$(COPYINST)
+ifneq ($(SUBDIRS),NONE)
+all preinstall libinstall dep depend: Make.Common
+all:     	$(SUBDIRS:%=%.d)
+preinstall:	$(SUBDIRS:%=%.d.preinstall)
+libinstall:	$(SUBDIRS:%=%.d.libinstall)
+install: 	$(SUBDIRS:%=%.d.install)
+titles: 	$(SUBDIRS:%=%.d.titles)
+clean:   	$(SUBDIRS:%=%.d.clean)
+dep depend:	$(SUBDIRS:%=%.d.depend)
+execlist:	$(SUBDIRS:%=%.d.execlist)
+endif
+#
+# Define rule patterns %.d.TARGET, which mean go into directory % and
+# build TARGET.  The % can get replaced by all dirs in $SUBDIRS.
+#
+%.d:		; $(MAKE) -f $(MAKEFILE) -C $(@:%.d=%) all
+%.d.preinstall:	; $(MAKE) -f $(MAKEFILE) -C $(@:%.d.preinstall=%) preinstall
+%.d.libinstall:	; $(MAKE) -f $(MAKEFILE) -C $(@:%.d.libinstall=%) libinstall
+%.d.install:	; $(MAKE) -f $(MAKEFILE) -C $(@:%.d.install=%) install
+%.d.clean:	; $(MAKE) -f $(MAKEFILE) -C $(@:%.d.clean=%) clean
+%.d.depend:	; $(MAKE) -f $(MAKEFILE) -C $(@:%.d.depend=%) depend
+%.d.execlist:	; @$(MAKE) -s -f $(MAKEFILE) -C $(@:%.d.execlist=%) execlist
+%.d.titles:	; @$(MAKE) -f $(MAKEFILE) -C $(@:%.d.titles=%) titles || \
+	        sh -c "cd $(@:%.d.titles=%) ; titler $(NEEDTITLES)"
+#
+# Regenerate Make.Common symlinks if they were lost by SVN.
+#
+Make.Common:
+	ln -s ../Make.Common $@
+else
+#
+#       --- Standard Targets for program and library directories ---
+#
+all: checkbin checkdep $(ALL)
+preinstall: $(HDRINST)
+install: checkbin checkdep $(ALLINST)
+world: ; $(MAKE) -f $(MAKEFILE) -C.. all install
+clean: ; -rm -rf $(DIR_OBS)/$(PROJECT)
+ifeq ($(PROJECT), $(PROJBASE))
+execlist: ; @echo $(PROJBASE)
+else
+# This target exists for the "mama-make" pass which only installs
+# libraries.  (After that, the mama-make gets all the programs.)
+libinstall: install
+endif
+#
+# Check if there are any dependencies in the Makefile, if not, run `make dep'
+#
+checkdep: ; @fgrep -e '# Dependencies by Make.Common $$Revision: 2.17 $$' $(MAKEFILE) > /dev/null || $(MAKE) -f $(MAKEFILE) dep
+#
+# Auto-generate dependencies.  This is very GNU C dependent.  The -MM
+# option tells gcc to only generate dependencies for files that are
+# #include'd with ""'s and not <> or system include files.  (So it is
+# generally ok to recompile on a different architecture without remaking
+# the dependencies.)
+#
+# Loop through each file in $SRCS and stick obj/ in front of each
+# .o dependency that gcc outputs (it does not have a way to tell it that
+# our .o's are not going into the current directory, and I couldn't get
+# it to work right with vpath.)  Strip off full paths to DIR_INC since
+# vpath *does* handle this correctly, allowing the same dependencies to
+# be re-used even if DIR_INC is in a different place on a different machine.
+#
+dep depend:
+	@fgrep -e "# Dependencies" $(MAKEFILE) > /dev/null || \
+	echo "# Dependencies" >> $(MAKEFILE)
+	@-rm -rf .Mtmp
+	sed -e 's/^\#\ Dependencies.*$$/\#\ Dependencies by Make.Common $$Revision: 2.17 $$/' -e '/^\#\ Dependencies/q' < $(MAKEFILE) > $(MAKEFILE).New
+	if [ "$(SRCS)" != "" ]; then for i in $(SRCS); do 		\
+	  ( echo;echo '$$(OBJ)/' )| dd bs=8 count=1 2>/dev/null>>.Mtmp;	\
+          DIRNAME=`dirname $$i`; [ $$DIRNAME != "." ] && echo -n $$DIRNAME/ >>.Mtmp; \
+	  $(CMM) -MM $(CFLAGS) $$i >> .Mtmp || exit 1;			\
+	  done;								\
+	  sed 's|$(DIR_INC)/||g' < .Mtmp >> $(MAKEFILE).New;		\
+	  sed 's| /[^ ]*.h||g' < $(MAKEFILE).New > .Mtmp;               \
+	  mv .Mtmp $(MAKEFILE).New;                                     \
+	  mv $(MAKEFILE) $(MAKEFILE).bak;				\
+	  mv $(MAKEFILE).New $(MAKEFILE);				\
+	fi
+	@-rm -rf .Mtmp
+endif
+#
+# Make a tar file snapshot of a current directory tree.
+#
+tar tgz: ; ( PD=`pwd`; PDV=`basename $$PD -$(VERSION)`-$(VERSION); cd .. ; \
+	$(TAR) -czvf $$PDV.tgz --exclude "*~" --exclude "#*#" \
+	--exclude "obj" --exclude "obj.*" \
+	--exclude "*.orig" --exclude "*.rej" --exclude "*.bak" \
+	--exclude ".xvpics" --exclude ".svn" --exclude "RCS" `basename $$PD` )
+
+#
+# Make sure that the $(DIR_OBS)/$(PROJECT) dir at least exists, which is
+# where programs, libraries, and/or .o's go before they are installed.
+# Directories are created with group write permissions.  The ./obj link
+# will be re-created if it doesn't point to a real directory after the
+# $(DIR_OBS)/$(PROJECT) is created.  This also checks that $(DIR_BIN) exists,
+# which is not really correct, but other targets fail if its not done now :-(
+#
+# The ./obj link is now just for convenience.  The makefile rules
+# do not require this to succeed, so it is possible to build with
+# "src" mounted read-only, as long as someone did "make dep" first.
+#
+# 2002-8-1: Clean the project OBS directory if it was previously used for
+#   another version (alternative would be to use /cfht/obs/proj-version).
+#
+checkbin:
+	@test -d $(DIR_BIN) || $(INSTALL) -m 0775 -d $(DIR_BIN)
+	@test -d $(DIR_OBS)/$(PROJECT) || $(INSTALL) -m 0775 -d $(DIR_OBS)/$(PROJECT)
+	@test -d $(OBJ_LINK) || ( rm -rf $(OBJ_LINK) 2> /dev/null && \
+	             ln -s $(DIR_OBSREL)/$(PROJECT) $(OBJ_LINK) ) 2> /dev/null \
+		     || true
+	@if [ "$(VERSION)" != "$(VERSION_DATE)" ]; then 		\
+	   if [ ! -r "$(DIR_OBS)/$(PROJECT)/version-$(VERSION)" ]; then	\
+	     rm -rf $(DIR_OBS)/$(PROJECT)/* ; 				\
+	     touch "$(DIR_OBS)/$(PROJECT)/version-$(VERSION)" ;		\
+	   fi ; 							\
+	fi
+
+# Final installed version of a program depends on the versioned installed
+# program.  This has to depend on the special target .PRECIOUS so that GNU
+# make will not see the versioned file as a temporary and try to remove it
+# at the end of a make install.  See the echo "--> ..." lines in the rest
+# of these targets for a description of what they install.
+#
+# The install line for "program" includes a -s, which strips the executable
+# at the same time that it is copied into the main bin/ directory.
+#
+.PRECIOUS: $(DIR_BIN)/%-$(VERSION)
+
+$(DIR_BIN)/%:	$(DIR_BIN)/%-$(VERSION)
+	@echo "--> Installing link to $(notdir $<) as $(notdir $@)"
+	@-mv $@ $@.$(shell date +%H%M%S).TEXT_FILE_BUSY 2> /dev/null || /bin/true
+	@-rm -f $(DIR_BIN)/*.TEXT_FILE_BUSY 2> /dev/null || /bin/true
+	@ln $< $@
+	# @-setuidinst $@
+
+$(DIR_BIN)/%-$(VERSION): scripts/%.sh
+	@echo "--> Installing sh script $(notdir $@)"
+	@-mv $@ $@.$(shell date +%H%M%S).TEXT_FILE_BUSY 2> /dev/null || /bin/true
+	@$(INSTALL) -m 0555 $< $@ || ( rm -f $@ && exit 1 )
+
+$(DIR_BIN)/%-$(VERSION): $(EXECNAME)
+	@echo "--> Installing program $(notdir $@)"
+	@-mv $@ $@.$(shell date +%H%M%S).TEXT_FILE_BUSY 2> /dev/null || /bin/true
+	@$(INSTSTRIP) -m 0755 $< $@ || ( rm -f $@ && exit 1 )
+	@chmod 0555 $@
+
+$(LIBINST):	$(LIBNAME)
+	@echo "--> Installing archive library $(notdir $<)"
+	@test -d $(DIR_LIB) || $(INSTALL) -m 0775 -d $(DIR_LIB)
+	@$(INSTALL) -m 0444 $< $@ || ( rm -f $@ && exit 1 )
+
+$(SHLIBINST):	$(SHLIBNAME)
+	@echo "--> Installing shared library $(SHLIBNAME)"
+	@test -d $(DIR_LIB) || $(INSTALL) -m 0775 -d $(DIR_LIB)
+	@$(INSTALL) -m 0555 $< $@ || ( rm -f $@ && exit 1 )
+
+$(DIR_CONF)/%: conf/%
+	@echo "--> Installing conf file $(notdir $<)"
+	@test -d $(DIR_CONF) || $(INSTALL) -m 0775 -d $(DIR_CONF)
+	@$(INSTALL) -m 0444 $< $@ || ( rm -f $@ && exit 1 )
+
+$(DIR_MAN)/man1/%: man/%
+	@echo "--> Installing man page $(notdir $<)"
+	@test -d $(DIR_MAN) || $(INSTALL) -m 0775 -d $(DIR_MAN)
+	@test -d $(DIR_MAN)/man1 || $(INSTALL) -m 0775 -d $(DIR_MAN)/man1
+	@$(INSTALL) -m 0444 $< $@ || ( rm -f $@ && exit 1 )
+
+# %%% The rm -f $(DIR_INC)/cfht/foo.h is a temporary hack to remove header
+# files from the old location, so that other programs don't accidentally
+# find an old copy of the header file if their #include statements haven't
+# been updated from "cfht/foo.h" to "libname/foo.h".
+$(DIR_INC)/$(PROJBASE)/%: %
+	@echo "--> Installing header file $<"
+	@rm -f $(DIR_INC)/cfht/$<
+	@test -d $(DIR_INC)/$(PROJBASE) || $(INSTALL) -m 0775 -d $(DIR_INC)/$(PROJBASE)
+	@$(INSTALL) -m 0444 $< $@ || ( rm -f $@ && exit 1 )
+endif
+#
+# Local settings (at CFHT, we want to compile with -Werror for most projects)
+#
+ifeq ($(DIR_SRC)/Make.Local,$(wildcard $(DIR_SRC)/Make.Local))
+include $(DIR_SRC)/Make.Local
+endif
+#
+# End of Make.Common
Index: /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/Makefile.in
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/Makefile.in	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/Makefile.in	(revision 23594)
@@ -0,0 +1,43 @@
+
+default: all
+
+all: burntool
+
+install: all
+	cp -f bin/* @BINDIR@/
+	cp -f man/man1/* @MANDIR@/man1/
+
+update:
+	mkdir -p gpcsrc
+# top-level files:
+	svn export https://svn.ifa.hawaii.edu/gpc/repos/sw/trunk/Makefile gpcsrc/Makefile
+	svn export https://svn.ifa.hawaii.edu/gpc/repos/sw/trunk/Make.Common gpcsrc/Make.Common
+	svn export https://svn.ifa.hawaii.edu/gpc/repos/sw/trunk/ThisIsTopLevel gpcsrc/ThisIsTopLevel
+	svn export https://svn.ifa.hawaii.edu/gpc/repos/sw/trunk/missing_protos.h gpcsrc/missing_protos.h
+# remove existing directories
+	rm -rf gpcsrc/fits
+	rm -rf gpcsrc/analysis
+# required subdirs
+	svn export https://svn.ifa.hawaii.edu/gpc/repos/sw/trunk/analysis/libpscoords gpcsrc/analysis/libpscoords
+	svn export https://svn.ifa.hawaii.edu/gpc/repos/sw/trunk/analysis/libpsf gpcsrc/analysis/libpsf
+	svn export https://svn.ifa.hawaii.edu/gpc/repos/sw/trunk/fits/libfh gpcsrc/fits/libfh
+	svn export https://svn.ifa.hawaii.edu/gpc/repos/sw/trunk/fits/libfhreg gpcsrc/fits/libfhreg
+	svn export https://svn.ifa.hawaii.edu/gpc/repos/sw/trunk/fits/burntool gpcsrc/fits/burntool
+# replace the standard gpc/Make.Common with our repaired version (g77 -> gfortran)
+	cp -f Make.Common.fixed gpcsrc/Make.Common
+# set up links
+	ln -sf ../Make.Common gpcsrc/analysis/Make.Common
+	ln -sf ../Make.Common gpcsrc/analysis/libpscoords/Make.Common
+	ln -sf ../Make.Common gpcsrc/analysis/libpsf/Make.Common
+	ln -sf ../Make.Common gpcsrc/fits/Make.Common
+	ln -sf ../Make.Common gpcsrc/fits/libfh/Make.Common
+	ln -sf ../Make.Common gpcsrc/fits/libfhreg/Make.Common
+	ln -sf ../Make.Common gpcsrc/fits/burntool/Make.Common
+
+burntool:
+	cd gpcsrc && make -C fits/libfh install
+	cd gpcsrc && make -C fits/libfhreg install
+	cd gpcsrc && make -C analysis/libpscoords install
+	cd gpcsrc && make -C analysis/libpsf install
+	cd gpcsrc && make -C fits/burntool install
+
Index: /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/autogen.sh
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/autogen.sh	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/autogen.sh	(revision 23594)
@@ -0,0 +1,13 @@
+#!/bin/sh
+
+for arg in $*; do
+    case $arg in
+        --no-configure)
+	    exit 0
+            ;;
+        *)
+            ;;
+    esac
+done
+
+./configure.tcsh $*
Index: /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/config.tools
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/config.tools	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/config.tools	(revision 23594)
@@ -0,0 +1,47 @@
+#!/bin/csh -f
+
+if ($#argv == 0) then
+  echo "USAGE: config.tools [fixpath | fixconf]"
+  exit 2
+endif
+
+if ("$argv[1]" == "fixpath") goto fixpath
+if ("$argv[1]" == "fixconf") goto fixconf
+
+echo "unknown option $argv[1]"
+exit 1
+
+#######
+fixpath:
+
+if ($#argv != 2) then
+  echo "USAGE: config.tools fixpath (path)"
+  exit 2
+endif
+
+set indir = $argv[2]
+
+# convert // to / in pathnames
+echo $indir | grep "\/\/" > /dev/null
+set success = $status
+while ($success == 0) 
+  set indir = `echo $indir | sed 's|\/\/|\/|g'`
+  echo $indir | grep "\/\/" > /dev/null
+  set success = $status
+end
+
+set indir = `echo $indir | sed 's|\/$||'`
+echo $indir
+exit 0
+
+#######
+fixconf:
+
+if ($#argv != 3) then
+  echo "USAGE: config.tools fixconf (NAME) (value)"
+  exit 2
+endif
+
+cat Makefile | sed "s|$argv[2]|$argv[3]|" > Makefile.tmp
+mv -f Makefile.tmp Makefile
+exit 0;
Index: /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/configure
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/configure	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/configure	(revision 23594)
@@ -0,0 +1,42 @@
+#!/bin/sh
+
+# strip out CC, CFLAGS, CPPFLAGS, LDFLAGS and set env vars
+while ( test $# -gt 0 ); do
+
+  skip=0
+
+  # strip out CC, set as env variable
+  echo $1 | grep "^CC=" > /dev/null
+  if ( test $? -eq 0 ) ; then
+    val=`echo $1 | sed "s|^CC=||"`
+    export CC=$val
+    skip=1
+  fi
+  # strip out CFLAGS, set as env variable
+  echo $1 | grep "^CFLAGS=" > /dev/null
+  if ( test $? -eq 0 ) ; then
+    val=`echo $1 | sed "s|^CFLAGS=||"`
+    export CFLAGS=$val
+    skip=1
+  fi
+  # strip out CPPFLAGS, set as env variable
+  echo $1 | grep "^CPPFLAGS=" > /dev/null
+  if ( test $? -eq 0 ) ; then
+    val=`echo $1 | sed "s|^CPPFLAGS=||"`
+    export CPPFLAGS=$val
+    skip=1
+  fi
+  # strip out LDFLAGS, set as env variable
+  echo $1 | grep "^LDFLAGS=" > /dev/null
+  if ( test $? -eq 0 ) ; then
+    val=`echo $1 | sed "s|^LDFLAGS=||"`
+    export LDFLAGS=$val
+    skip=1
+  fi
+  if ( test $skip -eq 0 ) ; then
+    args="$args $1"
+  fi
+  shift
+done
+
+./configure.tcsh $args
Index: /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/configure.tcsh
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/configure.tcsh	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/configure.tcsh	(revision 23594)
@@ -0,0 +1,179 @@
+#!/bin/csh -f
+
+# this is a very low-tech version of configure, not built by autoconf.
+# we check for the following libraries:
+
+# we need to be able to list the required libraries for a given distribution
+
+# evaluate command-line options
+set prefix  = ""
+set bindir  = ""
+set libdir  = ""
+set incdir  = ""
+set mandir  = ""
+set datadir  = ""
+set sysconfdir  = ""
+set exec_prefix = ""
+set defines = ""
+set profile = 0
+
+set root    = ""
+set args    = ""
+
+while ("$1" != "") 
+ switch ("$1")
+  # switch options passed by build systems which we ignore
+  case --enable-maintainer-mode
+  case --no-create
+  case --no-recursion
+  case --enable-optimize
+  case --disable-shared
+  case --enable-shared
+  case --disable-static
+  case --enable-static
+  case --enable-profile
+  case --pedantic
+   shift
+   breaksw;
+  # key/value options passed by build systems which we ignore
+  case --sbindir*
+  case --libexecdir*
+  case --sharedstatedir*
+  case --localstatedir*
+  case --oldincludedir*
+  case --infodir*
+  case --exec-prefix*
+  case --libdir*
+  case --includedir*
+  case --sysconfdir*
+  case --datadir*
+   # we need to strip the --opt word and --opt=word versions
+   set word = `echo $1 | tr = ' '`
+   if ($#word == 1) then
+     if ($#argv > 1) then
+      shift
+     endif
+   endif
+   breaksw;
+  case --prefix*
+   if ("$1" == "--prefix") then
+     shift
+     set prefix = $1
+   else
+     set prefix = `echo $1 | tr = ' ' | awk '{print $2}'`
+   endif
+   breaksw;
+  case --bindir*
+   if ("$1" == "--bindir") then
+     shift
+     set bindir = $1
+   else
+     set bindir = `echo $1 | tr = ' ' | awk '{print $2}'`
+   endif
+   breaksw;
+  case --mandir*
+   if ("$1" == "--mandir") then
+     shift
+     set mandir = $1
+   else
+     set mandir = `echo $1 | tr = ' ' | awk '{print $2}'`
+   endif
+   breaksw;
+  case --help:
+   goto usage
+  case -*: 
+   echo ""
+   echo "Unknown option: $1"
+   goto usage
+  default:
+   set args=($args $1);
+   breaksw;
+ endsw
+ shift
+end
+if ($#args != 1) goto usage
+
+# gpc build ignores CC, CFLAGS, CPPFLAGS, LDFLAGS
+
+# set up the basic directory names:
+set root = `pwd`
+if ($prefix == "") set prefix = $root
+
+echo 
+echo "install destinations:"
+echo "ROOT: $root"
+echo "PREFIX: $prefix"
+echo 
+
+# the config.tools fixconf operations below interpolate values in Makefile
+if (-e Makefile) mv Makefile Makefile.bak
+cp -f Makefile.in Makefile
+
+# BINDIR holds the output binary files
+if ("$bindir" == "") then
+  set subdir = bin
+  set bindir = $prefix/$subdir
+endif
+set bindir = `./config.tools fixpath $bindir`
+./config.tools fixconf @BINDIR@ $bindir
+echo BINDIR $bindir
+
+# MANDIR (DESTMAN) holds the output man pages
+if ("$mandir" == "") then
+  set mandir = $prefix/man
+endif
+set mandir = `./config.tools fixpath $mandir`
+./config.tools fixconf @MANDIR@ $mandir
+echo DESTMAN $mandir
+
+echo ""
+echo "include $bindir in your path"
+
+exit 0
+
+usage:
+cat <<EOF
+USAGE: configure [OPTION]
+
+echo remaining args: $args
+
+set the installation directory root with --prefix
+if you define the environment variable ARCH, you can set --vararch
+ 
+Configuration:
+  -h, --help              display this help and exit
+  --enable-optimize       enable compiler optimization (-O2)
+  --enable-memcheck       enable ohana memory tests
+  --pedantic              include -Wall -Werror on compilation
+
+Installation directories:
+  --prefix=PREFIX         install architecture-independent files in PREFIX
+  --vararch               install with ARCH suffixes for variable architectures
+
+Fine tuning of the installation directories:
+  --bindir=DIR           user executables [PREFIX/bin/$ARCH] 
+  --libdir=DIR           object code libraries [PREFIX/lib/$ARCH]
+  --includedir=DIR       C header files [PREFIX/include]
+  --mandir=DIR           man documentation [PREFIX/man]
+  --datadir=DIR          read-only architecture-independent data [PREFIX/share]
+  --sysconfdir=DIR       read-only single-machine data [PREFIX/etc]
+
+Makefile flags:
+  CC=options
+  CFLAGS=options
+  CPPFLAGS=options
+  LDFLAGS=options
+
+The following options are silently ignored for compatibility:
+  --enable-maintainer-mode
+  --no-create
+  --no-recursion
+  --sbindir
+  --libexecdir
+  --sharedstatedir
+  --localstatedir
+  --oldincludedir
+  --infodir
+
+EOF
+ exit 2;
Index: /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/Make.Common
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/Make.Common	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/Make.Common	(revision 23594)
@@ -0,0 +1,726 @@
+#
+# $Id: Make.Common,v 2.17 2004/09/05 10:10:45 thomas Exp $
+# $Locker:  $
+#
+# 2006-2-25: Windows cross-compilation support (not in CFHT RCS.)
+# 2006-4-19: "PREFIX" patch added by Sidik (not in CFHT RCS.)
+#
+# Make.Common - include at top of Makefiles for some common defaults.
+# Override any variables by setting them after including this file.
+# Then include Make.Common a second time, followed by any project
+# specific targets. For example, a typical library Makefile looks like this:
+#
+#   # Makefile for libfoo
+#   include ../Make.Common
+#   SRCS=foo1.c foo2.c foo3.c
+#   HDRS=foo.h
+#   include ../Make.Common
+#
+# If SRCS just equals *.c and *.cc (recommended) then you can
+# omit it completely from your makefile.  When a new C file
+# appears in the directory for libfoo, it will then automatically
+# be added to libfoo on the next build.  HDRS gives the header
+# files you want to install.  If you do not have any internal .h
+# files in your library, you can omit HDRS as well and *.h is assumed.
+# For a program, a typical Makefile looks like this:
+#
+#   # Makefile for foo
+#   include ../Make.Common
+#   VERSION=1.1
+#   $(EXECNAME): $(OBJS) libfoo.a libcfht.a
+#   include ../Make.Common
+#
+# Again, if you don't want it to pick up *.c and *.cc, then you might
+# want to define SRCS explicitly (OBJS will be generated for you).
+# If you omit VERSION, the current date (YYMMDD) is used instead.
+# And for a project toplevel directory, a Makefile might look like:
+#
+#   # Makefile for foo project directory
+#   include ../Make.Common
+#   SUBDIRS=libfoo foo
+#   foo.d: libfoo.d.install
+#   include ../Make.Common
+#
+# Use this last template whenever creating a subdirectory off of
+# the main directory which contains Make.Common.  Create a symlink
+# in the new directory so that the "leaves" (programs and libraries)
+# can always include "../Make.Common" from their Makefiles.
+#
+# The dependency line "foo.d: libfoo.d.install" indicates that libfoo
+# must be _installed_ before foo is _built_.  Other dependencies
+# within a program are generated automatically by "make dep".  It
+# only generates dependencies for files included with "" (quotes)
+# and not <>, so if want your program to rebuild if cfht.h changes,
+# include it with "cfht/cfht.h" instead of <cfht/cfht.h>.
+#
+# More user information can be found at http://software/make.html
+# The rest of the comments in this file mostly pertain to hacking
+# on Make.Common itself.
+#
+# ---------------------------------------------------------------------------
+ifndef HAVE_VARS
+#
+# On the first pass, define defaults for makefile variables.
+# Then the user gets a chance to override any, and finally
+# Make.Common is included again.
+#
+HAVE_VARS := True
+#
+# Default is to build everything, but not install it (except that
+# some libraries may get installed if other projects depend on them).
+#
+default: all
+#
+#                          --- Directories ---
+#
+# Find out where to store the architecture dependent objects.
+# Apr 2000: Added some temporary transitional hacks to help migration.
+#           Remove /tmp_mnt, /local, /usr/local/cfht to /cfht, /cfht/dev -> /cfht/src
+#           This might break building at other sites!
+# 
+FIND_ROOT := $(shell DA=`pwd`; DR="obs"; \
+while [ ! -f $$DA/ThisIsTopLevel -a "$$DA" != "/" ]; do \
+  DA=`cd $$DA/..;pwd | sed -e 's,^/local/,/,' -e 's,/tmp_mnt/,/,' -e 's,^/usr/local/cfht/,/cfht/,' -e 's,/cfht/dev,/cfht/src,'`; DR=../$$DR; \
+done; \
+if [ "$$DA" = "/" ]; then \
+echo "ERROR: You must create an empty file called \`ThisIsTopLevel' in a common" 1>&2;\
+echo "  directory shared by project sources.  e.g.: /usr/local/src/ThisIsTopLevel" 1>&2;\
+echo "  The directory one level higher will be used as a base for lib,bin,include" 1>&2;\
+echo "  e.g. /usr/local/{bin,lib,...} if you create /usr/local/src/ThisIsTopLevel." 1>&2;\
+echo "*** Please press ^C now and create this file before continuing..." 1>&2;\
+read Dmy; else \
+echo `cd $$DA/..;pwd | sed -e 's,^/local/,/,' -e 's,/tmp_mnt/,/,' -e 's,^/usr/local/cfht/,/cfht/,' -e 's,/cfht/dev,/cfht/src,'` ../$$DR $$DA; fi)
+
+#
+# This defines that basic paths that are commonly used during the build
+# process.  DIR_GNU must be the prefix where you've installed gcc,
+# gmake, and ginstall.  The rest are all install locations for header
+# files, man pages, libraries, objects, and programs.
+#
+DIR_GNU   := /apps/gnu
+PREFIX    := $(word 1,$(FIND_ROOT))
+DIR_OBSREL:= $(word 2,$(FIND_ROOT))
+DIR_SRC   := $(word 3,$(FIND_ROOT))
+#
+# Allow local settings to override PREFIX.  Make.Local is
+# included again at the very end, allowing any other
+# variables to be overridden.
+#
+ifeq ($(DIR_SRC)/Make.Local,$(wildcard $(DIR_SRC)/Make.Local))
+include $(DIR_SRC)/Make.Local
+endif
+#
+DIR_ROOT  := $(PREFIX)
+DIR_CONF  := $(DIR_ROOT)/conf
+DIR_BIN   := $(DIR_ROOT)/bin
+DIR_LIB   := $(DIR_ROOT)/lib
+DIR_MAN   := $(DIR_ROOT)/man
+DIR_INC   := $(DIR_ROOT)/include
+DIR_OBS   := $(DIR_ROOT)/obs
+#
+#                         --- Build Tools ---
+#
+# MAKEFILE may be useful during migration (if a project needs two styles
+# of makefiles at once.)  PATH should have at least DIR_GNU and DIR_BIN.
+# Rest are just the basic programs used to build the program.  User is
+# expected to run GNU Make 3.74 or better.  After that, everything here
+# should take care of making sure the right versions of things are run.
+#
+MAKEFILE = Makefile
+PATH     := .:$(DIR_GNU)/bin:$(DIR_BIN):/usr/local/bin:/usr/5bin:/usr/bin:/bin:/usr/ucb:$(PATH)
+SHELL	 = /bin/sh
+INSTALL  = ginstall
+INSTSTRIP= $(INSTALL) -s
+TAR	 = gtar
+AR	 = ar
+FC       = gfortran
+CC	 = gcc
+LD       = gcc
+CXX	 = g++
+CMM	 = g++	# for make depend
+PURIFY   = purify
+#
+#                       --- Build Tool Flags ---
+#
+# Standard flags to ar and gcc during compile/link phases.  The GNU compiler
+# allows -g and -O at the same time.  At installation, the executables are
+# stripped and become the equivalent of just a "-O" compiled version.  The
+# copy with the symbols stays in $(DIR_OBS) until the next make or make clean.
+#
+# The setting of CCWARN here gives messages comparable to most things that
+# lint used to check for.  Override in the project makefile by setting it
+# to nothing if it's too noisy.
+#
+# The flags in CCSHARE apply to two different things: -fPIC is used during
+# compilation and -shared applies to the link stage.
+#
+FCDEBUG  = -g -O -fno-automatic
+FFWARN   = -Wall
+FFLAGS   = $(FCDEBUG) $(EXTRA_FFLAGS) $(FFWARN) $(CCDEFS) $(CCINCS)
+CCDEBUG  = -g -O
+CCSHARE  = -fPIC -shared
+CCWARN	 = -Wall -Wstrict-prototypes # -Wshadow
+CFLAGS   = $(CCDEBUG) $(EXTRA_CFLAGS) $(CCWARN) $(CCDEFS) $(CCINCS) $(CCHACKS)
+LDFLAGS  = $(CCDEBUG) $(EXTRA_CFLAGS) $(CCLIBS)
+CXXFLAGS = $(subst -Wstrict-prototypes,,$(CFLAGS))
+ARFLAGS  = -rc
+#
+# Find out the host name and type that we are currently running on.
+# TARGET=OSname-OSmajor. No support is provided for cross-compilers.
+# Later in this file TARGET is looked at and is used to define
+# one of: -DHPUX -DCYGWIN32 -DLINUX -DSOLARIS or -DSUNOS.  If absolutely
+# necessary, use this for conditional compiles in your code.  If
+# further distinctions are needed for other OSes or OS versions,
+# make up a new define and add it to the section that checks TARGET.
+#
+HOSTNAME := $(strip $(shell hostname))
+ifeq ($(TARGET),)
+TARGET := $(shell echo `uname -s`-`uname -r | cut -d . -f 1`)
+endif
+DOMAIN := $(shell domainname 2> /dev/null || echo "unknown")
+#
+# At CFHT, make sure stuff gets installed as group `daprog':
+#
+ifeq ($(DOMAIN),cfht)
+INSTALL += -g daprog
+endif
+#
+# Override or += these variables in the project's Makefile
+#
+VERSION_DATE := $(shell date +%y%m%d | sed -e 's/:/0/')
+VERSION      = $(VERSION_DATE)
+EXTRA_CFLAGS =
+#
+# Include the following defines by adding CCDEFS+=$(VERSIONDEFS) to Makefile:
+#
+VERSIONDEFS=-DVERSION=$(VERSION) -DSOURCEDATE="\"`sourcedate . 2> /dev/null`\"" -DBUILDDATE="\"`date +'%b %d %Y'`\""
+#
+# Provide a HINT for programs that can use either syslog or cfht_log
+#
+ifeq ($(origin NO_CFHTLOG), undefined)
+NO_CFHTLOG   := $(shell if [ ! -p /tmp/pipes/syslog.np ];then echo "-DNO_CFHTLOG";fi)
+endif
+#
+# Don't build in support for EPICS except on saturn and neptune (RPM & hform)
+#
+ifeq ($(origin USE_EPICS), undefined)
+USE_EPICS    := $(shell if [ "$(HOSTNAME)" = "saturn" -o "$(HOSTNAME)" = "neptune" ]; then echo "-DUSE_EPICS";fi)
+endif
+#
+CCDEFS       = $(NO_CFHTLOG) $(USE_EPICS)
+CCINCS       = -I. -Iinclude -I$(DIR_INC)
+CCLIBS       = -L$(DIR_LIB)
+CCHACKS      =
+#
+#                        --- Program Files ---
+#
+# The *INST variables end up containing the list of targets as their
+# installed names with the full paths like $(DIR_INC) and $(DIR_CONF)
+# tacked on to each filename.  NEEDTITLES is a list of files which
+# Sidik's titler program should attempt to place a copyright header in.
+#
+SRCS	     = $(shell ls *.f *.f77 *.c *.cc 2> /dev/null)
+HDRS	     = $(shell ls *.h *.hh 2> /dev/null)
+HDRINST	     = $(HDRS:%=$(DIR_INC)/$(PROJBASE)/%)
+MAN1         = $(shell cd ./man 2>/dev/null && /bin/ls *.1 2>/dev/null)
+MAN2         = $(shell cd ./man 2>/dev/null && /bin/ls *.2 2>/dev/null)
+MAN3         = $(shell cd ./man 2>/dev/null && /bin/ls *.3 2>/dev/null)
+MAN4         = $(shell cd ./man 2>/dev/null && /bin/ls *.4 2>/dev/null)
+MAN5         = $(shell cd ./man 2>/dev/null && /bin/ls *.5 2>/dev/null)
+MAN6         = $(shell cd ./man 2>/dev/null && /bin/ls *.6 2>/dev/null)
+MAN7         = $(shell cd ./man 2>/dev/null && /bin/ls *.7 2>/dev/null)
+MAN8         = $(shell cd ./man 2>/dev/null && /bin/ls *.8 2>/dev/null)
+CONFS        = $(shell cd ./conf 2>/dev/null && /bin/ls *.def *.par *.bm *.xbm *.rdb *.xrdb 2>/dev/null)
+SCRIPTS      = $(shell cd ./scripts 2>/dev/null && /bin/ls *.sh 2>/dev/null)
+COPYINST     = $(CONFS:%=$(DIR_CONF)/%) $(SCRIPTS:%.sh=$(DIR_BIN)/%) \
+	$(MAN1:%=$(DIR_MAN)/man1/%) \
+	$(MAN2:%=$(DIR_MAN)/man2/%) \
+	$(MAN3:%=$(DIR_MAN)/man3/%) \
+	$(MAN4:%=$(DIR_MAN)/man4/%) \
+	$(MAN5:%=$(DIR_MAN)/man5/%) \
+	$(MAN6:%=$(DIR_MAN)/man6/%) \
+	$(MAN7:%=$(DIR_MAN)/man7/%) \
+	$(MAN8:%=$(DIR_MAN)/man8/%)
+NEEDTITLES   = $(shell ls * | egrep -v "^\#|~$$" 2> /dev/null)
+#
+# SUBDIRS gets overridden only by Makefiles that just build other
+# subdirectories in their tree.  These directory nodes cannot generate
+# a program or a library (this is only done at the `leaves')
+#
+SUBDIRS      =
+#
+#                       --- System Dependent ---
+#
+# Some typical places we might find X11, typical extension for shared lib,
+# and extension for executables.
+#
+CCINCSX11 = -I/usr/X11/include
+CCLIBSX11 = -L/usr/X11/lib
+CCLINKX11 = -lX11 -lXext
+OBJ_LINK = obj
+EXE =
+SL  = .so
+#
+# Some overrides for a Win32 system running a cygnus win32 gcc
+#
+ifeq ($(TARGET), CYGWIN32_NT-4)
+CCDEFS   += -DCYGWIN32
+EXE       = .exe
+SL        = .dll
+endif
+#
+# Some overrides for HPUX (X11 in non-standard place, .sl for shared libs)
+#
+ifeq ($(TARGET), HP-UX-A)
+CCINCSX11 = -I/usr/include/X11R5
+CCLIBSX11 = -L/usr/lib/X11R5
+CCLINKX11 = -lX11
+CCDEFS   += -DHPUX -DHACK_XLIBS -DHACK_SELECT
+# X Libraries may have bugs, so X clients (hform) that really care
+# about this should add /usr/local/lib/wm (a CFHT-ism) to SHLIB_PATH.
+# select() prototype is broken, and missing_protos.h should fix it.
+CCHACKS   = -fwritable-strings
+SL        = .sl
+endif
+#
+# HP-UX 10.20 no longer has the scanf bug in the C library, so
+# doesn't need -fwritable-strings anymore.  X is also up to R6 now.
+#
+ifeq ($(TARGET), HP-UX-B)
+CCINCSX11 = -I/usr/include/X11R6
+CCLIBSX11 = -L/usr/lib/X11R6
+CCLINKX11 = -lX11
+CCDEFS   += -DHPUX
+SL        = .sl
+endif
+#
+# Linux often has dns in a separate libresolv and crypt in libcrypt.
+# Some installations require them to be explicitly linked.
+# Linux's utilities (tar, install) are the GNU versions, so no need
+# for the `g' prefix which we use on other platforms to ensure that
+# we're getting the GNU versions.
+#
+ifeq ($(TARGET), Linux-2)
+TAR = tar
+INSTALL = install
+CCINCSX11 = -I/usr/X11R6/include
+CCLIBSX11 = -L/usr/X11R6/lib
+CCLINKX11 = -lX11
+CCLINK  = -Wl,-R,$(DIR_LIB)
+ifeq (/usr/lib/libresolv.so,$(wildcard /usr/lib/libresolv.so))
+CCLINKNET += -lresolv
+endif
+ifeq (/usr/lib/libcrypt.so,$(wildcard /usr/lib/libcrypt.so))
+CCLINKNET += -lcrypt
+endif
+CCDEFS   += -DLINUX
+endif
+#
+# Solaris needs to link with libsocket and libnsl.  Also X11 is with openwin.
+#
+ifeq ($(TARGET), SunOS-5)
+CCINCSX11 = -I/usr/openwin/include
+CCLIBSX11 = -L/usr/openwin/lib
+CCLINKNET = -lsocket -lnsl
+# Without this the Solaris dynamic linker may not be able to find shared
+# versions of libg++ and libstdc++.  SunOS builds it from the -L's.
+CCLINK  = -Wl,-R,$(DIR_GNU)/lib
+CCDEFS   += -DSOLARIS
+endif
+#
+# Old suns are missing EXIT_FAILURE and EXIT_SUCCESS from stdlib.h
+#
+ifeq ($(TARGET), SunOS-4)
+CCDEFS   += -DSUNOS -D__USE_FIXED_PROTOTYPES__ -DEXIT_FAILURE=1 -DEXIT_SUCCESS=0
+endif
+#
+# Cross-compiler for VxWorks on a Sparc
+#
+ifeq ($(TARGET), VXSPARC)
+AR      = vxarsparc
+CC      = vxgccsparc
+LD      = vxgccsparc
+CXX     = vxg++sparc
+CMM	= vxg++sparc	# for make depend
+CROSSCC = vxsparc
+OBJ_LINK     = obj.vxsparc
+CCDEFS += -DCPU=SPARC -DVXWORKS -Dmain=$(PROJECT)
+DIR_BIN   := $(DIR_BIN)/$(CROSSCC)
+DIR_LIB   := $(DIR_LIB)/$(CROSSCC)
+DIR_OBS   := $(DIR_OBS)/$(CROSSCC)
+DIR_OBSREL:= $(DIR_OBSREL)/$(CROSSCC)
+NO_CFHTLOG:= -DNO_CFHTLOG
+INSTSTRIP= $(INSTALL)
+endif
+#
+# Cross-compiler for VxWorks on a PowerPC
+#
+ifeq ($(TARGET), VXPOWERPC)
+AR      = vxarpowerpc
+CC      = vxgccpowerpc
+LD      = vxgccpowerpc
+CXX     = vxg++powerpc
+CMM	= vxgccpowerpc	# for make depend, change to ++ if vxg++powerpc works
+CROSSCC = vxpowerpc
+OBJ_LINK     = obj.vxpowerpc
+CCDEFS += -DCPU=PPC604 -DVXWORKS -Dmain=$(PROJECT)
+DIR_BIN   := $(DIR_BIN)/$(CROSSCC)
+DIR_LIB   := $(DIR_LIB)/$(CROSSCC)
+DIR_OBS   := $(DIR_OBS)/$(CROSSCC)
+DIR_OBSREL:= $(DIR_OBSREL)/$(CROSSCC)
+NO_CFHTLOG:= -DNO_CFHTLOG
+INSTSTRIP= $(INSTALL)
+endif
+#
+# Cross-compiler for PowerPC405 (embedded)
+#
+ifeq ($(TARGET), PPC405)
+AR      = /apps/ppc405/powerpc-405-linux-gnu/gcc-3.4.2-glibc-2.2.5/bin/powerpc-405-linux-gnu-ar
+CC      = /apps/ppc405/powerpc-405-linux-gnu/gcc-3.4.2-glibc-2.2.5/bin/powerpc-405-linux-gnu-gcc
+LD      = /apps/ppc405/powerpc-405-linux-gnu/gcc-3.4.2-glibc-2.2.5/bin/powerpc-405-linux-gnu-gcc
+CXX     = /apps/ppc405/powerpc-405-linux-gnu/gcc-3.4.2-glibc-2.2.5/bin/powerpc-405-linux-gnu-g++
+CMM	= /apps/ppc405/powerpc-405-linux-gnu/gcc-3.4.2-glibc-2.2.5/bin/powerpc-405-linux-gnu-g++
+CROSSCC = ppc405
+OBJ_LINK     = obj.ppc405
+CCDEFS += -DPPC405
+DIR_BIN   := $(DIR_BIN)/$(CROSSCC)
+DIR_LIB   := $(DIR_LIB)/$(CROSSCC)
+DIR_OBS   := $(DIR_OBS)/$(CROSSCC)
+DIR_OBSREL:= $(DIR_OBSREL)/$(CROSSCC)
+NO_CFHTLOG:= -DNO_CFHTLOG
+INSTSTRIP= $(INSTALL)
+endif
+#
+# Cross-compiler for Windows i386
+#
+ifeq ($(TARGET), MINGW32)
+PATH   := /usr/local/cross-tools/bin:$(PATH)
+AR      = i386-mingw32msvc-ar
+CC      = i386-mingw32msvc-gcc
+LD      = i386-mingw32msvc-gcc
+CXX     = i386-mingw32msvc-g++
+CMM     = i386-mingw32msvc-g++
+CROSSCC = mingw32
+OBJ_LINK     = obj.mingw32
+EXE       = .exe
+SL        = .dll
+CCDEFS += -DMINGW32
+#DIR_BIN   := $(DIR_BIN)/$(CROSSCC)
+DIR_LIB   := $(DIR_LIB)/$(CROSSCC)
+DIR_OBS   := $(DIR_OBS)/$(CROSSCC)
+DIR_OBSREL:= $(DIR_OBSREL)/$(CROSSCC)
+NO_CFHTLOG:= -DNO_CFHTLOG
+INSTSTRIP= $(INSTALL)
+endif
+#
+#                  --- Project and Target Names ---
+#
+OBJ	  = $(DIR_OBS)/$(PROJECT)
+#
+# OBJS1 is just a temporary variable with *.cc converted to *.o and
+# OBJS contains *.cc + *.c -> *.o.  OBJS_PIC lists the names that
+# would be used if a shared library is being build (*.pic.o, in the
+# same directory where the other *.o files went.
+#
+OBJS3    = $(SRCS:%.cc=$(OBJ)/%.o)
+OBJS2	 = $(OBJS3:%.c=$(OBJ)/%.o)
+OBJS1    = $(OBJS2:%.f=$(OBJ)/%.o)
+OBJS     = $(OBJS1:%.f77=$(OBJ)/%.o)
+OBJS_PIC = $(OBJS:%.o=%.pic.o)
+#
+# If a program or library is generated, the subdirectory MUST have the
+# same name as the program it generates.  This is placed in PROJECT.
+# PROJBASE contains the same thing, unless it started with `lib', in
+# which case the lib is trimmed off.  This is how we know if we are
+# building a library or not.
+#
+PROJECT   = $(patsubst %-$(VERSION),%,$(notdir $(shell pwd)))
+PROJBASE  = $(PROJECT:lib%=%)
+#
+# `make all' causes the program to be deposited in this temporary directory
+#
+EXECNAME  = $(OBJ)/$(PROJBASE)$(EXE)
+#
+# `make install' strips it and copies it to the main bin/ directory.
+#
+EXECINST  = $(DIR_BIN)/$(PROJBASE)$(EXE)
+#
+# `make all' for libraries first assembles the .a here
+#
+LIBNAME   = $(OBJ)/$(PROJECT).a
+#
+# `make install' for libraries copies it unstripped to the lib/ directory
+#
+LIBINST   = $(DIR_LIB)/$(PROJECT).a
+#
+# Same all over again, except for the shared versions of the libraries.
+#
+SHLIBNAME = $(OBJ)/$(PROJECT)$(SL)
+SHLIBINST = $(DIR_LIB)/$(PROJECT)$(SL)
+#
+# This is where we detect if this is a library or a program
+#
+ifeq ($(PROJECT), $(PROJBASE))
+ALL	= $(EXECNAME)
+ALLINST = $(COPYINST) $(EXECINST)
+HDRINST =
+else
+ALL	= $(LIBNAME)
+# ALL += $(SHLIBNAME)
+ALLINST = $(HDRINST) $(LIBINST)
+# ALLINS += $(SHLIBINST)
+endif
+#
+# ------------------------- End of Variables -----------------------------
+else
+# -------------------------- Start of Rules ------------------------------
+#
+# This section gets read during the second pass.  It defines the standard
+# rules defined at http://software/make.html, using the variables defined
+# above (possibly with some overrides that the user put in between.)
+#
+.SUFFIXES: ; # Do not use default targets like '.c.o' and '.cc.o'
+#
+# Tell make never to expect a file by any of these names:
+#
+.PHONY:	clean all dep default checkdep checkbin preinstall libinstall install execlist execlist-sh
+#
+vpath %.h     $(DIR_INC)/cfht:$(DIR_INC)	# include files
+vpath %.hh    $(DIR_INC)			# C++ include files
+vpath %.bm    $(DIR_INC)			# bitmap include files
+vpath %.a     $(DIR_LIB)			# libraries
+vpath %$(SL)  $(DIR_LIB)			# shared libraries
+#
+#                        --- Rule Patterns ---
+#
+# These are just the standard ways to cause gcc to generate a .o from a .c,
+# g++ to generate a .o from a .cc, and to create libraries with ar (or
+# gcc in the case of shared libraries... which we are not really using yet.)
+#
+$(OBJ)/%.o: %.f          ; $(FC)  $(FFLAGS)   -c $*.f  -o $@
+$(OBJ)/%.o: %.c          ; $(CC)  $(CFLAGS)   -c $*.c  -o $@
+$(OBJ)/%.o: %.cc         ; $(CXX) $(CXXFLAGS) -c $*.cc -o $@
+$(OBJ)/%.pic.o: %.f      ; $(CC)  $(CFLAGS)   $(CCSHARE) -c $*.f  -o $@
+$(OBJ)/%.pic.o: %.c      ; $(CC)  $(CFLAGS)   $(CCSHARE) -c $*.c  -o $@
+$(OBJ)/%.pic.o: %.cc     ; $(CXX) $(CXXFLAGS) $(CCSHARE) -c $*.cc -o $@
+$(LIBNAME):  $(OBJS)      ; rm -f $@ ; $(AR) $(ARFLAGS) $@~ $^ ; mv $@~ $@
+$(SHLIBNAME): $(OBJS_PIC) ; rm -f $@ ; $(LD) $(LDFLAGS) $(CCSHARE) $^ $(CCLINK) -o $@
+#
+# The cryptic substitution that starts with $(^... takes all the dependencies
+# that this executable has and replaces anything like /usr/local/lib/libcfht.a
+# with a -lcfht.  WARNING: if a shared version exists, it will be used!
+#
+$(EXECNAME):
+	$(LD) $(LDFLAGS) $(^:$(DIR_LIB)/lib%.a=-l%) $(CCLINK) -o $@~
+	@mv $@~ $@
+#
+# NOTE: Use `make obj/myprogram-pure' to invoke this.  Also be sure you have
+#       $(EXECNAME) $(EXECNAME)-pure: in your project Makefile
+#
+$(EXECNAME)-pure: ; $(PURIFY) $(LD) $(LDFLAGS) $^ $(CCLINK) -o $@
+#
+# Sidik's titler program will insert comment header blocks into all your
+# source files if you create the proper Index files.
+#
+titles: ; @titler $(NEEDTITLES)
+#
+# `make execlist' at any level produces a list of all the "executables"
+# (scripts or compiled C programs) from the current level down.  It is
+# used by the version programs for pegasus accounts.
+#
+execlist: execlist-sh
+execlist-sh:
+	@for i in "" $(SCRIPTS:%.sh=%); do if [ "$$i" != "" ]; then echo $$i; fi; done
+
+ifneq ($(SUBDIRS),)
+#
+#         --- Standard Targets for project parent directories ---
+#                                    (See http://software/make.html)
+world: all install
+install:	$(COPYINST)
+ifneq ($(SUBDIRS),NONE)
+all preinstall libinstall dep depend: Make.Common
+all:     	$(SUBDIRS:%=%.d)
+preinstall:	$(SUBDIRS:%=%.d.preinstall)
+libinstall:	$(SUBDIRS:%=%.d.libinstall)
+install: 	$(SUBDIRS:%=%.d.install)
+titles: 	$(SUBDIRS:%=%.d.titles)
+clean:   	$(SUBDIRS:%=%.d.clean)
+dep depend:	$(SUBDIRS:%=%.d.depend)
+execlist:	$(SUBDIRS:%=%.d.execlist)
+endif
+#
+# Define rule patterns %.d.TARGET, which mean go into directory % and
+# build TARGET.  The % can get replaced by all dirs in $SUBDIRS.
+#
+%.d:		; $(MAKE) -f $(MAKEFILE) -C $(@:%.d=%) all
+%.d.preinstall:	; $(MAKE) -f $(MAKEFILE) -C $(@:%.d.preinstall=%) preinstall
+%.d.libinstall:	; $(MAKE) -f $(MAKEFILE) -C $(@:%.d.libinstall=%) libinstall
+%.d.install:	; $(MAKE) -f $(MAKEFILE) -C $(@:%.d.install=%) install
+%.d.clean:	; $(MAKE) -f $(MAKEFILE) -C $(@:%.d.clean=%) clean
+%.d.depend:	; $(MAKE) -f $(MAKEFILE) -C $(@:%.d.depend=%) depend
+%.d.execlist:	; @$(MAKE) -s -f $(MAKEFILE) -C $(@:%.d.execlist=%) execlist
+%.d.titles:	; @$(MAKE) -f $(MAKEFILE) -C $(@:%.d.titles=%) titles || \
+	        sh -c "cd $(@:%.d.titles=%) ; titler $(NEEDTITLES)"
+#
+# Regenerate Make.Common symlinks if they were lost by SVN.
+#
+Make.Common:
+	ln -s ../Make.Common $@
+else
+#
+#       --- Standard Targets for program and library directories ---
+#
+all: checkbin checkdep $(ALL)
+preinstall: $(HDRINST)
+install: checkbin checkdep $(ALLINST)
+world: ; $(MAKE) -f $(MAKEFILE) -C.. all install
+clean: ; -rm -rf $(DIR_OBS)/$(PROJECT)
+ifeq ($(PROJECT), $(PROJBASE))
+execlist: ; @echo $(PROJBASE)
+else
+# This target exists for the "mama-make" pass which only installs
+# libraries.  (After that, the mama-make gets all the programs.)
+libinstall: install
+endif
+#
+# Check if there are any dependencies in the Makefile, if not, run `make dep'
+#
+checkdep: ; @fgrep -e '# Dependencies by Make.Common $$Revision: 2.17 $$' $(MAKEFILE) > /dev/null || $(MAKE) -f $(MAKEFILE) dep
+#
+# Auto-generate dependencies.  This is very GNU C dependent.  The -MM
+# option tells gcc to only generate dependencies for files that are
+# #include'd with ""'s and not <> or system include files.  (So it is
+# generally ok to recompile on a different architecture without remaking
+# the dependencies.)
+#
+# Loop through each file in $SRCS and stick obj/ in front of each
+# .o dependency that gcc outputs (it does not have a way to tell it that
+# our .o's are not going into the current directory, and I couldn't get
+# it to work right with vpath.)  Strip off full paths to DIR_INC since
+# vpath *does* handle this correctly, allowing the same dependencies to
+# be re-used even if DIR_INC is in a different place on a different machine.
+#
+dep depend:
+	@fgrep -e "# Dependencies" $(MAKEFILE) > /dev/null || \
+	echo "# Dependencies" >> $(MAKEFILE)
+	@-rm -rf .Mtmp
+	sed -e 's/^\#\ Dependencies.*$$/\#\ Dependencies by Make.Common $$Revision: 2.17 $$/' -e '/^\#\ Dependencies/q' < $(MAKEFILE) > $(MAKEFILE).New
+	if [ "$(SRCS)" != "" ]; then for i in $(SRCS); do 		\
+	  ( echo;echo '$$(OBJ)/' )| dd bs=8 count=1 2>/dev/null>>.Mtmp;	\
+          DIRNAME=`dirname $$i`; [ $$DIRNAME != "." ] && echo -n $$DIRNAME/ >>.Mtmp; \
+	  $(CMM) -MM $(CFLAGS) $$i >> .Mtmp || exit 1;			\
+	  done;								\
+	  sed 's|$(DIR_INC)/||g' < .Mtmp >> $(MAKEFILE).New;		\
+	  sed 's| /[^ ]*.h||g' < $(MAKEFILE).New > .Mtmp;               \
+	  mv .Mtmp $(MAKEFILE).New;                                     \
+	  mv $(MAKEFILE) $(MAKEFILE).bak;				\
+	  mv $(MAKEFILE).New $(MAKEFILE);				\
+	fi
+	@-rm -rf .Mtmp
+endif
+#
+# Make a tar file snapshot of a current directory tree.
+#
+tar tgz: ; ( PD=`pwd`; PDV=`basename $$PD -$(VERSION)`-$(VERSION); cd .. ; \
+	$(TAR) -czvf $$PDV.tgz --exclude "*~" --exclude "#*#" \
+	--exclude "obj" --exclude "obj.*" \
+	--exclude "*.orig" --exclude "*.rej" --exclude "*.bak" \
+	--exclude ".xvpics" --exclude ".svn" --exclude "RCS" `basename $$PD` )
+
+#
+# Make sure that the $(DIR_OBS)/$(PROJECT) dir at least exists, which is
+# where programs, libraries, and/or .o's go before they are installed.
+# Directories are created with group write permissions.  The ./obj link
+# will be re-created if it doesn't point to a real directory after the
+# $(DIR_OBS)/$(PROJECT) is created.  This also checks that $(DIR_BIN) exists,
+# which is not really correct, but other targets fail if its not done now :-(
+#
+# The ./obj link is now just for convenience.  The makefile rules
+# do not require this to succeed, so it is possible to build with
+# "src" mounted read-only, as long as someone did "make dep" first.
+#
+# 2002-8-1: Clean the project OBS directory if it was previously used for
+#   another version (alternative would be to use /cfht/obs/proj-version).
+#
+checkbin:
+	@test -d $(DIR_BIN) || $(INSTALL) -m 0775 -d $(DIR_BIN)
+	@test -d $(DIR_OBS)/$(PROJECT) || $(INSTALL) -m 0775 -d $(DIR_OBS)/$(PROJECT)
+	@test -d $(OBJ_LINK) || ( rm -rf $(OBJ_LINK) 2> /dev/null && \
+	             ln -s $(DIR_OBSREL)/$(PROJECT) $(OBJ_LINK) ) 2> /dev/null \
+		     || true
+	@if [ "$(VERSION)" != "$(VERSION_DATE)" ]; then 		\
+	   if [ ! -r "$(DIR_OBS)/$(PROJECT)/version-$(VERSION)" ]; then	\
+	     rm -rf $(DIR_OBS)/$(PROJECT)/* ; 				\
+	     touch "$(DIR_OBS)/$(PROJECT)/version-$(VERSION)" ;		\
+	   fi ; 							\
+	fi
+
+# Final installed version of a program depends on the versioned installed
+# program.  This has to depend on the special target .PRECIOUS so that GNU
+# make will not see the versioned file as a temporary and try to remove it
+# at the end of a make install.  See the echo "--> ..." lines in the rest
+# of these targets for a description of what they install.
+#
+# The install line for "program" includes a -s, which strips the executable
+# at the same time that it is copied into the main bin/ directory.
+#
+.PRECIOUS: $(DIR_BIN)/%-$(VERSION)
+
+$(DIR_BIN)/%:	$(DIR_BIN)/%-$(VERSION)
+	@echo "--> Installing link to $(notdir $<) as $(notdir $@)"
+	@-mv $@ $@.$(shell date +%H%M%S).TEXT_FILE_BUSY 2> /dev/null || /bin/true
+	@-rm -f $(DIR_BIN)/*.TEXT_FILE_BUSY 2> /dev/null || /bin/true
+	@ln $< $@
+	# @-setuidinst $@
+
+$(DIR_BIN)/%-$(VERSION): scripts/%.sh
+	@echo "--> Installing sh script $(notdir $@)"
+	@-mv $@ $@.$(shell date +%H%M%S).TEXT_FILE_BUSY 2> /dev/null || /bin/true
+	@$(INSTALL) -m 0555 $< $@ || ( rm -f $@ && exit 1 )
+
+$(DIR_BIN)/%-$(VERSION): $(EXECNAME)
+	@echo "--> Installing program $(notdir $@)"
+	@-mv $@ $@.$(shell date +%H%M%S).TEXT_FILE_BUSY 2> /dev/null || /bin/true
+	@$(INSTSTRIP) -m 0755 $< $@ || ( rm -f $@ && exit 1 )
+	@chmod 0555 $@
+
+$(LIBINST):	$(LIBNAME)
+	@echo "--> Installing archive library $(notdir $<)"
+	@test -d $(DIR_LIB) || $(INSTALL) -m 0775 -d $(DIR_LIB)
+	@$(INSTALL) -m 0444 $< $@ || ( rm -f $@ && exit 1 )
+
+$(SHLIBINST):	$(SHLIBNAME)
+	@echo "--> Installing shared library $(SHLIBNAME)"
+	@test -d $(DIR_LIB) || $(INSTALL) -m 0775 -d $(DIR_LIB)
+	@$(INSTALL) -m 0555 $< $@ || ( rm -f $@ && exit 1 )
+
+$(DIR_CONF)/%: conf/%
+	@echo "--> Installing conf file $(notdir $<)"
+	@test -d $(DIR_CONF) || $(INSTALL) -m 0775 -d $(DIR_CONF)
+	@$(INSTALL) -m 0444 $< $@ || ( rm -f $@ && exit 1 )
+
+$(DIR_MAN)/man1/%: man/%
+	@echo "--> Installing man page $(notdir $<)"
+	@test -d $(DIR_MAN) || $(INSTALL) -m 0775 -d $(DIR_MAN)
+	@test -d $(DIR_MAN)/man1 || $(INSTALL) -m 0775 -d $(DIR_MAN)/man1
+	@$(INSTALL) -m 0444 $< $@ || ( rm -f $@ && exit 1 )
+
+# %%% The rm -f $(DIR_INC)/cfht/foo.h is a temporary hack to remove header
+# files from the old location, so that other programs don't accidentally
+# find an old copy of the header file if their #include statements haven't
+# been updated from "cfht/foo.h" to "libname/foo.h".
+$(DIR_INC)/$(PROJBASE)/%: %
+	@echo "--> Installing header file $<"
+	@rm -f $(DIR_INC)/cfht/$<
+	@test -d $(DIR_INC)/$(PROJBASE) || $(INSTALL) -m 0775 -d $(DIR_INC)/$(PROJBASE)
+	@$(INSTALL) -m 0444 $< $@ || ( rm -f $@ && exit 1 )
+endif
+#
+# Local settings (at CFHT, we want to compile with -Werror for most projects)
+#
+ifeq ($(DIR_SRC)/Make.Local,$(wildcard $(DIR_SRC)/Make.Local))
+include $(DIR_SRC)/Make.Local
+endif
+#
+# End of Make.Common
Index: /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/Makefile
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/Makefile	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/Makefile	(revision 23594)
@@ -0,0 +1,58 @@
+# Top-level Makefile to GigaPixelCamera project tree
+
+default: world-message
+include Make.Common
+world-message:
+	@echo "This is the top-level Makefile for GigaPixelCamera stuff."
+	@echo "NOTE: \`make' commands will result in:"
+	@echo ""
+	@echo "Temporary objects : $(DIR_OBS)     (can be removed to save space)"
+	@echo "Include files     : $(DIR_INC) (not required at run-time)"
+	@echo "Static libraries  : $(DIR_LIB)     (not required at run-time)"
+	@echo "Executables       : $(DIR_BIN)     (add this to your PATH)"
+	@echo ""
+	@echo "Relocate (mv) this directory tree to a place other than $(DIR_ROOT)"
+	@echo "if this is not the desired place for installation."
+	@echo ""
+	@echo "Type \`make world' to build and install everything."
+	@echo "Running \`make dep' first may also be necessary."
+	@echo ""
+	@/bin/false
+
+# Add new modules here, if they are to build automatically.  See Index.
+SUBDIRS=maketools network general fits facility detector
+
+world:	install
+	@echo "Congratulations!  All software successfully installed."
+
+# Before installing, build:
+install: all
+
+# But find all libraries and install them before building any programs:
+all: libinstall
+dep depend: preinstall
+
+# And before building libraries, install the header files, etc.
+libinstall: preinstall
+preinstall: $(DIR_INC)/missing_protos.h checkdirs
+
+# Deal with autofs if necessary for first-time builds.
+# This does not do anything if you do not use automounts.
+checkdirs:
+	for i in $(DIR_CONF) $(DIR_BIN) $(DIR_LIB) $(DIR_INC) $(DIR_OBS); do \
+	  ls $$i > /dev/null 2>&1 ; \
+	  test -L $$i && mkdir -p `/bin/ls -l $$i | sed -e 's/.*-> //'`||true;\
+	done
+
+# missing_protos.h is a hack that doesn't really belong anywhere.
+#
+$(DIR_INC)/missing_protos.h: missing_protos.h
+	@echo "--> Installing header file $<"
+	@test -d $(DIR_INC) || $(INSTALL) -m 0775 -d $(DIR_INC)
+	@$(INSTALL) -m 0444 $< $@
+
+# Create an empty missing_protos.h if none is needed
+missing_protos.h:
+	@test -f $@ || touch $@
+
+include Make.Common
Index: /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/analysis/Make.Common
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/analysis/Make.Common	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/analysis/Make.Common	(revision 23594)
@@ -0,0 +1,1 @@
+link ../Make.Common
Index: /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/analysis/libpscoords/Make.Common
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/analysis/libpscoords/Make.Common	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/analysis/libpscoords/Make.Common	(revision 23594)
@@ -0,0 +1,1 @@
+link ../Make.Common
Index: /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/analysis/libpscoords/Makefile
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/analysis/libpscoords/Makefile	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/analysis/libpscoords/Makefile	(revision 23594)
@@ -0,0 +1,6 @@
+include ../Make.Common
+CCWARN+=$(WERROR)
+include ../Make.Common
+# Dependencies by Make.Common $Revision: 2.17 $
+
+$(OBJ)/pscoords.o: pscoords.c pscoords.h
Index: /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/analysis/libpscoords/pscoords.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/analysis/libpscoords/pscoords.c	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/analysis/libpscoords/pscoords.c	(revision 23594)
@@ -0,0 +1,469 @@
+/* Routine to convert microns in the focal plane to OTA/cell index and posn */
+/* Syntax: pscoords [args] < infile > outfile
+ *  args:
+ *     in=[sky | tp | fp | ota | cell]
+ *     out=[sky | tp | fp | ota | cell]
+ *
+ *   and other arguments required for various transformations: see syntax()
+ */
+/* See PSDC-730-003-01 (OTA FITS) and PSDC-730-005-00 (GPC Arch) for details */
+/* 080930 Sidik added TEST_MAIN - must be defined if you want main() test program. */
+/* 080904 Rev 1.1, renamed from fpota.c */
+/* 071023 Rev 1.0 John Tonry */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <math.h>
+#include "pscoords.h"
+
+static char
+#if defined(__GNUC__) && defined(__STDC__)
+__attribute__ ((__unused__))
+#endif
+rcsid[] = "$Id: pscoords.c,v 1.2 2008/09/06 03:26:48 jt Exp jt $";
+
+/* Overall focal plane offset and rotation (um, rad) */
+const PSC_OFFROT_T psc_psc_fpoff={0.,0.,0.};
+
+/* Offsets and rotations of each OTA (um, millirad) */
+const PSC_OFFROT_T psc_otaoff[PSC_NX*PSC_NY]={	/* Offsets and rotations derived o4885 090228 */
+   {   0.0,   0.0,  0.00}, {  20.2, -59.5, -1.23},  /* OTA00 , OTA10 */
+   {  28.1,-138.1,  0.18}, {   2.0, -43.6, -1.22},  /* OTA20 , OTA30 */
+   {  33.6,  91.5, -0.38}, {  20.3,  48.8, -0.81},  /* OTA40 , OTA50 */
+   {  22.5, -67.5, -1.48}, {   0.0,   0.0,  0.00},  /* OTA60 , OTA70 */
+   {   4.8, -29.1,  0.83}, { -51.8, -51.7,  0.95},  /* OTA01 , OTA11 */
+   {  31.8,  -8.6, -0.68}, { -13.7,  11.3, -0.82},  /* OTA21 , OTA31 */
+   {  -2.3,  58.7,  0.92}, {   5.5,  67.5, -0.26},  /* OTA41 , OTA51 */
+   { -11.8,   3.1, -1.55}, {  -3.0,   1.2,  0.36},  /* OTA61 , OTA71 */
+   { -26.1,  40.6,  2.75}, {   0.4, -53.7,  0.53},  /* OTA02 , OTA12 */
+   {  26.6, -56.8, -1.51}, {  19.4,-178.7,  1.78},  /* OTA22 , OTA32 */
+   { -22.5,   4.8, -1.04}, { -14.4,  -1.9,  0.06},  /* OTA42 , OTA52 */
+   {  19.2,  -7.8, -2.04}, {  89.9,  34.9,  1.79},  /* OTA62 , OTA72 */
+   { -24.6,  20.4,  0.15}, {-103.2, -84.4,  1.98},  /* OTA03 , OTA13 */
+   { 110.1,  11.1,  1.11}, {  12.6, -24.9,  0.53},  /* OTA23 , OTA33 */
+   { -50.5,  34.5, -0.73}, { -11.0,  20.0,  0.49},  /* OTA43 , OTA53 */
+   {  17.2, -29.8,  1.52}, {  27.9, -72.3, -0.84},  /* OTA63 , OTA73 */
+   { -43.7,   1.5,  0.23}, { -21.4, -11.6,  0.23},  /* OTA04 , OTA14 */
+   {  64.5, 208.5, -0.36}, { -48.1, -91.1,  0.98},  /* OTA24 , OTA34 */
+   { -14.2,  16.8,  0.01}, { -41.3,   1.9,  0.01},  /* OTA44 , OTA54 */
+   {  10.8,  49.3,  1.34}, {  89.7,  -7.2,  0.43},  /* OTA64 , OTA74 */
+   { -29.2,  38.4,  2.74}, {  20.8,  -7.5, -0.59},  /* OTA05 , OTA15 */
+   { -34.1,  -7.2,  1.70}, { -16.2, -36.1,  0.02},  /* OTA25 , OTA35 */
+   { -51.9,  45.6, -0.60}, {  23.9,  82.4,  0.68},  /* OTA45 , OTA55 */
+   { -34.9,  57.1, -0.47}, {  72.4, -32.0,  0.68},  /* OTA65 , OTA75 */
+   {  16.2,  62.6,  0.17}, { -11.8,   7.5, -0.88},  /* OTA06 , OTA16 */
+   { -82.3, -39.5,  1.37}, {  11.2, -40.0,  0.56},  /* OTA26 , OTA36 */
+   {  -7.5,  31.1, -1.27}, {  26.4,  34.2,  0.26},  /* OTA46 , OTA56 */
+   {  14.1,   3.6, -1.42}, {  20.1,  58.0, -0.23},  /* OTA66 , OTA76 */
+   {   0.0,   0.0,  0.00}, {  50.4,  20.4, -0.47},  /* OTA07 , OTA17 */
+   {  77.9,  -1.5, -0.60}, { -35.0, -47.0,  0.68},  /* OTA27 , OTA37 */
+   { -74.8,   0.3, -0.92}, { -44.6,  -1.6, -1.78},  /* OTA47 , OTA57 */
+   { -50.3,  72.1, -0.47}, {   0.0,   0.0,  0.00}}; /* OTA67 , OTA77 */
+
+#ifdef RUN_TWO
+/* Offsets and rotations of each OTA (um, millirad) */
+const PSC_OFFROT_T psc_otaoff[PSC_NX*PSC_NY]={	/* Offsets and rotations derived o4699-o4724 */
+   {   0.0,   0.0,  0.0 },  {  57.4, -71.7,  0.41},  /* OTA00 , OTA10 */
+   {  35.0,-136.4,  0.29},  {  -7.6, -47.4,  2.20},  /* OTA20 , OTA30 */
+   {  -4.1,  75.8,  0.52},  {  13.9,  19.4, -1.73},  /* OTA40 , OTA50 */
+   {  27.3, -24.3,  0.62},  {   0.0,   0.0,  0.0 },  /* OTA60 , OTA70 */
+   {  26.3, -36.1,  0.57},  { -54.7, -89.1,  1.64},  /* OTA01 , OTA11 */
+   {  -1.1, -33.2, -1.09},  {  -4.7, -71.7, -0.25},  /* OTA21 , OTA31 */
+   {  25.6,  51.8,  0.62},  {   2.1,  55.6,  0.32},  /* OTA41 , OTA51 */
+   {  44.5,  41.5,  0.15},  {  53.8,  52.8,  0.33},  /* OTA61 , OTA71 */
+   {   0.9,  25.3,  2.30},  { -33.1, -63.5,  2.85},  /* OTA02 , OTA12 */
+   {  27.9, -87.0, -0.20},  {  62.6, -20.8,  0.50},  /* OTA22 , OTA32 */
+   { -35.4, -14.7, -1.42},  {  24.6,  53.9, -0.82},  /* OTA42 , OTA52 */
+   {  29.9,  36.7, -1.10},  { 100.5,  21.9,  1.27},  /* OTA62 , OTA72 */
+   {  -1.1,  26.8, -1.16},  {  31.0, -46.0,  3.07},  /* OTA03 , OTA13 */
+   { 113.2,  53.7,  0.39},  {  65.1, -17.3, -0.98},  /* OTA23 , OTA33 */
+   { -54.9,  12.8,  0.10},  {  19.9,  30.6,  1.08},  /* OTA43 , OTA53 */
+   {  35.3, -12.8,  1.68},  {  60.8,  30.3, -0.37},  /* OTA63 , OTA73 */
+   { -79.8, -28.2,  0.53},  { -50.8, -63.1,  0.21},  /* OTA04 , OTA14 */
+   {  33.4, -38.1, -0.18},  {   7.5, -89.4,  0.67},  /* OTA24 , OTA34 */
+   {  -9.7,  56.5, -0.71},  {  -2.4,  42.5,  0.42},  /* OTA44 , OTA54 */
+   {  -0.4,  32.8,  2.13},  {  23.1, -48.4,  0.41},  /* OTA64 , OTA74 */
+   { -87.1,   7.8,  1.24},  { -42.1, -62.0,  2.01},  /* OTA05 , OTA15 */
+   { -77.4,   2.4,  1.59},  { -38.4, -37.6,  1.00},  /* OTA25 , OTA35 */
+   { -23.2,  50.4, -1.81},  {   1.0,  58.4,  0.33},  /* OTA45 , OTA55 */
+   { -28.3,   7.1, -0.69},  {   3.8, -60.3, -0.15},  /* OTA65 , OTA75 */
+   { -20.0,  28.7,  1.57},  { -26.0,  45.6,  3.08},  /* OTA06 , OTA16 */
+   { -36.4, -69.0,  0.49},  {  26.1,  -4.9, -1.47},  /* OTA26 , OTA36 */
+   { -24.2,  98.6,  0.09},  { -51.5,  14.7, -0.88},  /* OTA46 , OTA56 */
+   {  16.6,  31.0, -1.34},  {   7.1,  12.4, -0.13},  /* OTA66 , OTA76 */
+   {   0.0,   0.0,  0.0 },  {  -6.5,  39.8, -0.03},  /* OTA06 , OTA17 */
+   { -15.7,   3.9,  0.65},  { -26.4,  16.5,  0.07},  /* OTA27 , OTA37 */
+   { -41.1,  76.3,  0.56},  { -60.8,  43.0, -0.16},  /* OTA47 , OTA57 */
+   { -52.6,  65.1,  0.58},  {   0.0,   0.0,  0.0 }}; /* OTA67 , OTA77 */
+#endif
+
+#ifdef FIRST_GUESS
+/* Offsets and rotations of each OTA (um, rad) */
+PSC_OFFROT_T psc_otaoff[PSC_NX*PSC_NY]={	/* Offsets and rotations derived o4678 */
+   {   0.0,   0.0, 0.00000},  {  46.8, -82.5, 0.00102},  /* OTA00 , OTA10 */
+   {  29.1,-130.8, 0.00145},  {   1.7, -46.3, 0.00315},  /* OTA20 , OTA30 */
+   {  14.4,  70.8, 0.00095},  {  20.0,  30.3,-0.00145},  /* OTA40 , OTA50 */
+   {   4.0,  -1.2, 0.00121},  {   0.0,   0.0, 0.00000},  /* OTA60 , OTA70 */
+   {  -2.9, -67.9, 0.00090},  { -98.0,-105.0, 0.00107},  /* OTA01 , OTA11 */
+   { -22.2, -23.0,-0.00171},  {  12.0, -72.2,-0.00056},  /* OTA21 , OTA31 */
+   {  57.1,  33.0, 0.00030},  {  18.6,  54.9,-0.00055},  /* OTA41 , OTA51 */
+   {  22.0,  80.7,-0.00063},  {  21.4,  81.2, 0.00027},  /* OTA61 , OTA71 */
+   { -16.9,  23.8, 0.00248},  { -32.3, -46.8, 0.00181},  /* OTA02 , OTA12 */
+   {  58.7, -45.0,-0.00045},  { 118.1,  -3.0, 0.00076},  /* OTA22 , OTA32 */
+   {  32.3, -48.0,-0.00113},  {  76.6,  25.5,-0.00131},  /* OTA42 , OTA52 */
+   {  46.2,  48.4,-0.00198},  { 104.5,  39.2, 0.00182},  /* OTA62 , OTA72 */
+   {  -1.2,  42.8,-0.00095},  {  43.2, -10.4, 0.00274},  /* OTA03 , OTA13 */
+   { 139.5, 114.3, 0.00086},  {  96.9,   7.9, 0.00011},  /* OTA23 , OTA33 */
+   { -18.8, -22.2, 0.00117},  {  43.1, -23.5, 0.00148},  /* OTA43 , OTA53 */
+   {  44.9, -33.1, 0.00138},  {  69.1,  25.9, 0.00006},  /* OTA63 , OTA73 */
+   { -87.3, -12.1, 0.00112},  { -48.1, -43.0, 0.00014},  /* OTA04 , OTA14 */
+   {  10.9,  20.4, 0.00041},  { -29.1, -59.1, 0.00185},  /* OTA24 , OTA34 */
+   { -44.8,  30.6, 0.00053},  { -25.0, -15.0, 0.00084},  /* OTA44 , OTA54 */
+   { -15.0,  -1.9, 0.00182},  {  18.0, -58.7, 0.00067},  /* OTA64 , OTA74 */
+   { -92.8,   9.3, 0.00202},  { -55.1, -63.4, 0.00168},  /* OTA05 , OTA15 */
+   {-125.6,  35.0, 0.00142},  {-112.7,  -6.6, 0.00137},  /* OTA25 , OTA35 */
+   { -90.2,  36.4,-0.00160},  { -34.4,  20.9, 0.00005},  /* OTA45 , OTA55 */
+   { -25.9, -11.9,-0.00160},  {  19.8, -59.5,-0.00020},  /* OTA65 , OTA75 */
+   {   1.3,  43.8, 0.00208},  {   0.2,  21.7, 0.00234},  /* OTA06 , OTA16 */
+   { -47.8, -61.4,-0.00023},  { -13.6,  21.8,-0.00201},  /* OTA26 , OTA36 */
+   { -50.1, 104.1,-0.00061},  { -34.3,  14.9,-0.00148},  /* OTA46 , OTA56 */
+   {  72.3,  53.8,-0.00213},  {  58.0,  47.6,-0.00012},  /* OTA66 , OTA76 */
+   {   0.0,   0.0, 0.00000},  {  31.1,  44.6, 0.00113},  /* OTA07 , OTA17 */
+   {  -8.2,   5.3, 0.00087},  { -39.8,  35.0, 0.00031},  /* OTA27 , OTA37 */
+   { -47.8,  85.1, 0.00135},  { -28.8,  51.0, 0.00037},  /* OTA47 , OTA57 */
+   {  -2.6,  91.5, 0.00071},  {   0.0,   0.0, 0.00000}}; /* OTA67 , OTA77 */
+#endif
+
+static int DOOFFSET;	/* Do these offsets or no? */
+
+#if 0
+#define IRAS	/* Gnomonic? irsa.ipac.edu/IRASdocs/surveys/coordproj.html */
+#define PSC_REFRACT_CONST 55.7	/* Standard refraction ("/tanz) at STP */
+#endif
+
+/* Enable application of chip offsets? */
+int psc_do_chipoff(int doit)
+{
+   DOOFFSET = doit;
+   return(0);
+}
+
+/* Convert a focal plane position to OTA otax,otay, Cell cellx,celly, Pixel */
+int psc_fp_to_pixel(double xfp, double yfp, 	/* FP position (um) */
+		int *ota_xid, int *ota_yid,	/* OTA ID (0:7) */
+		double *xota, double *yota)	/* OTA position (pix) */
+{
+   double mechgap=0.5*(PSC_HMECH-PSC_HDIE);	/* Assume equal L/R/T chip gap */
+   int idx;				/* Index of this OTA in otaoff */
+   int lhs;				/* Left/right hand side = +1/-1 */
+   double xc, yc, x, y, xp, yp;
+
+/* Position in FP after removing offset and rotation */
+   if(DOOFFSET) {
+      x =  (xfp-psc_fpoff.dx)*cos(psc_fpoff.rot) + (yfp-psc_fpoff.dy)*sin(psc_fpoff.rot);
+      y = -(xfp-psc_fpoff.dx)*sin(psc_fpoff.rot) + (yfp-psc_fpoff.dy)*cos(psc_fpoff.rot);
+   } else {
+      x = xfp;
+      y = yfp;
+   }
+
+/* Guess at which side we are on */
+   lhs = x > 0 ? +1 : -1;
+
+/* Guess at which OTA this is */
+   *ota_xid = NINT(PSC_NX/2-0.5-x/PSC_HMECH);
+   *ota_yid = NINT((y-lhs*0.5*(PSC_VMOFF+PSC_VMECH-PSC_VDIE-2*mechgap))/PSC_VMECH+(PSC_NY/2-0.5));
+/* Stop at the edges of the focal plane */
+   if (*ota_xid < 0)       *ota_xid = 0;
+   if (*ota_xid >= PSC_NX) *ota_xid = PSC_NX-1;
+   if (*ota_yid < 0)       *ota_yid = 0;
+   if (*ota_yid >= PSC_NY) *ota_yid = PSC_NY-1;
+   idx = *ota_xid+*ota_yid*PSC_NX;
+
+/* Center of nominal mechanical PSC_HMECH,PSC_VMECH envelope wrt FP center */
+   xc = ((PSC_NX/2-0.5)-*ota_xid) * PSC_HMECH;
+   yc = (*ota_yid-(PSC_NY/2-0.5)) * PSC_VMECH + lhs*0.5*(PSC_VMOFF+PSC_VMECH-PSC_VDIE-2*mechgap);
+
+/* Center of OTA (8x8 set of cells) within mechanical envelope wrt FP center */
+   xc -= lhs*(0.5*PSC_HMECH - mechgap - PSC_LBORDER - PSC_NX/2*PSC_HCELL - (PSC_NX/2-0.5)*PSC_VSTREET);
+   yc -= lhs*(0.5*PSC_VMECH - mechgap - PSC_TBORDER - PSC_NY/2*PSC_VCELL - (PSC_NY/2-0.5)*PSC_HSTREET);
+
+/* Position wrt nominal OTA center prior to offset and rotation correction */
+   xp = x - xc;
+   yp = y - yc;
+
+/* Position wrt OTA center after correction for offset and rotation */
+   if(DOOFFSET) {
+      x =  (xp-psc_otaoff[idx].dx)*cos(psc_otaoff[idx].rot*0.001) + 
+	 (yp-psc_otaoff[idx].dy)*sin(psc_otaoff[idx].rot*0.001);
+      y = -(xp-psc_otaoff[idx].dx)*sin(psc_otaoff[idx].rot*0.001) + 
+	 (yp-psc_otaoff[idx].dy)*cos(psc_otaoff[idx].rot*0.001);
+   } else {
+      x = xp;
+      y = yp;
+   }
+
+/* Position in pixels relative to OTA origin */
+   *xota =  (lhs*x + (PSC_NX/2*PSC_HCELL + (PSC_NX/2-0.5)*PSC_VSTREET)) / PSC_PIXEL;
+   *yota = -(lhs*y - (PSC_NY/2*PSC_VCELL + (PSC_NY/2-0.5)*PSC_HSTREET)) / PSC_PIXEL;
+
+   return(0);
+}
+
+/* Convert a pixel position in OTA ota_xid,ota_yid to FP */
+int psc_pixel_to_fp(int ota_xid, int ota_yid,	/* OTA ID (0:7) */
+		double xota, double yota,	/* OTA position (pix) */
+		double *xfp, double *yfp)	/* FP position (um) */
+{
+   int idx=ota_xid+ota_yid*PSC_NX;		/* Index of this OTA in otaoff */
+   double mechgap=0.5*(PSC_HMECH-PSC_HDIE);	/* Assume equal L/R/T chip gap */
+   int lhs=ota_xid < PSC_NX/2 ? +1 : -1;	/* Left/right hand side = +1/-1 */
+   double xc, yc, x, y, xp, yp;
+
+/* Pixel position in um relative to OTA center */
+   x = lhs * ( xota*PSC_PIXEL - (PSC_NX/2*PSC_HCELL + (PSC_NX/2-0.5)*PSC_VSTREET));
+   y = lhs * (-yota*PSC_PIXEL + (PSC_NY/2*PSC_VCELL + (PSC_NY/2-0.5)*PSC_HSTREET));
+
+/* Position wrt nominal OTA center after its offset and rotation */
+   if(DOOFFSET) {
+      xp = psc_otaoff[idx].dx + x*cos(psc_otaoff[idx].rot*0.001) - y*sin(psc_otaoff[idx].rot*0.001);
+      yp = psc_otaoff[idx].dy + x*sin(psc_otaoff[idx].rot*0.001) + y*cos(psc_otaoff[idx].rot*0.001);
+   } else {
+      xp = x;
+      yp = y;
+   }
+
+/* Center of nominal mechanical PSC_HMECH,PSC_VMECH envelope wrt FP center */
+   xc = ((PSC_NX/2-0.5)-ota_xid) * PSC_HMECH;
+   yc = (ota_yid-(PSC_NY/2-0.5)) * PSC_VMECH + lhs*0.5*(PSC_VMOFF+PSC_VMECH-PSC_VDIE-2*mechgap);
+
+/* Center of OTA (8x8 set of cells) within mechanical envelope wrt FP center */
+   xc -= lhs*(0.5*PSC_HMECH - mechgap - PSC_LBORDER - PSC_NX/2*PSC_HCELL - (PSC_NX/2-0.5)*PSC_VSTREET);
+   yc -= lhs*(0.5*PSC_VMECH - mechgap - PSC_TBORDER - PSC_NY/2*PSC_VCELL - (PSC_NY/2-0.5)*PSC_HSTREET);
+
+/* Position within FP */
+   x = xp + xc;
+   y = yp + yc;
+
+/* Final position from FP offset and rotation */
+   if(DOOFFSET) {
+      *xfp = psc_fpoff.dx + x*cos(psc_fpoff.rot) - y*sin(psc_fpoff.rot);
+      *yfp = psc_fpoff.dy + x*sin(psc_fpoff.rot) + y*cos(psc_fpoff.rot);
+   } else {
+      *xfp = x;
+      *yfp = y;
+   }
+
+   return(0);
+}
+
+/* Convert an OTA pixel position to cell ID and cell coords */
+int psc_pixel_to_cell(double xota, double yota,	/* OTA position (pix) */
+		  int *cell_xid, int *cell_yid,	/* Cell ID (0:7) or -1 */
+		  double *xcell, double *ycell)	/* Cell position (pix) */
+{
+   double x, y;
+   x = xota*PSC_PIXEL / (PSC_HCELL+PSC_VSTREET);
+   *cell_xid = (int)x;
+   *xcell = (x - (int)x) * (PSC_HCELL+PSC_VSTREET) / PSC_PIXEL;
+   if(x < 0 || *cell_xid > PSC_NX-1 || 
+      (x-*cell_xid) > 1-PSC_VSTREET/(PSC_HCELL+PSC_VSTREET)) *cell_xid = -1;
+   *xcell = PSC_HCELL/PSC_PIXEL - *xcell;
+
+   y = yota*PSC_PIXEL / (PSC_VCELL+PSC_HSTREET);
+   *cell_yid = (int)y;
+   *ycell = (y - (int)y) * (PSC_VCELL+PSC_HSTREET) / PSC_PIXEL;
+   if(y < 0 || *cell_yid > PSC_NY-1 ||
+      (y-*cell_yid) > 1-PSC_HSTREET/(PSC_VCELL+PSC_HSTREET)) *cell_yid = -1;
+
+   return(0);
+}
+
+/* Convert a cell position and ID to OTA coords */
+int psc_cell_to_pixel(int cell_xid, int cell_yid,	/* Cell ID (0:7) */
+		  double xcell, double ycell,	/* Cell position (pix) */
+		  double *xota, double *yota)	/* OTA position (pix) */
+{
+   *xota = (PSC_HCELL/PSC_PIXEL - xcell) + cell_xid*(PSC_HCELL+PSC_VSTREET) / PSC_PIXEL;
+   *yota = ycell + cell_yid*(PSC_VCELL+PSC_HSTREET) / PSC_PIXEL;
+   return(0);
+}
+
+/* Evaluate an optical model: arcsec -> microns notionally */
+/* Offset, scale with distortion, rotate */
+int psc_psoptics(double x, double y, double dx, double dy, double dpa, 
+	 double pscale, double d3, double *xfp, double *yfp)
+{
+   double w2=((x-dx)*(x-dx)+(y-dy)*(y-dy));
+   x = pscale * (1.0 + d3*w2) * (x-dx);
+   y = pscale * (1.0 + d3*w2) * (y-dy);
+/* Apply any extra TP-FP rotation */
+   *xfp =  x * cos(dpa) + y * sin(dpa);
+   *yfp = -x * sin(dpa) + y * cos(dpa);
+   return(0);
+}
+
+/* Evaluate an inverse optical model: microns -> arcsec notionally */
+/* Offset, scale with *small* distortion, rotate */
+int psc_invoptics(double xfp, double yfp, double dx, double dy, double dpa, 
+	  double pscale, double d3, double *x, double *y)
+{
+   double xp, yp;
+/* Undo any extra TP-FP rotation */
+   xp = (xfp * cos(dpa) - yfp * sin(dpa)) / pscale;
+   yp = (xfp * sin(dpa) + yfp * cos(dpa)) / pscale;
+   *x = xp / (1.0 + d3*(xp*xp+yp*yp)) + dx;
+   *y = yp / (1.0 + d3*(xp*xp+yp*yp)) + dy;
+   return(0);
+}
+
+
+/* Project RA,Dec (a,d) to the tangent plane (x,y) at (a0,d0,pa) [radians] */
+/* y is rotated CCW by pa from N; NOTE: x is west when PA = 0 (pos parity) */
+int psc_tproject(double a, double d, double a0, double d0, double pa,
+	 double density, double zenith, double vertical, 
+	 double *x, double *y)
+{
+#ifndef IRAS
+   double pdotx, pdoty, pdotz, E, N, dx, dy, z, Up, Horiz, sec2rad=atan(1.0)/(45*3600);
+   pdotx = cos(a0)*cos(d0)*cos(a)*cos(d) + 
+      sin(a0)*cos(d0)*sin(a)*cos(d) + sin(d0)*sin(d);
+   pdoty = -sin(a0)*cos(a)*cos(d) + cos(a0)*sin(a)*cos(d);
+   pdotz = -cos(a0)*sin(d0)*cos(a)*cos(d) -
+      sin(a0)*sin(d0)*sin(a)*cos(d) + cos(d0)*sin(d);
+   E = pdoty / pdotx;
+   N = pdotz / pdotx;
+   *x = -E*cos(pa) + N*sin(pa);
+   *y =  E*sin(pa) + N*cos(pa);
+/* Differential refraction? */
+   if(density > 0) {
+/* Rotate to vertical */
+      Horiz =  *x * cos(vertical) + *y * sin(vertical);
+      Up = -*x * sin(vertical) + *y * cos(vertical);
+      z = sqrt(Horiz*Horiz+(zenith-Up)*(zenith-Up));
+      dx = -density*sec2rad*PSC_REFRACT_CONST*tan(z)*Horiz/z;
+      dy = density*sec2rad*PSC_REFRACT_CONST*(tan(z)*(zenith-Up)/z - tan(zenith));
+      Horiz += dx;
+      Up += dy;
+/* Rotate back */
+      *x =  Horiz * cos(vertical) - Up * sin(vertical);
+      *y =  Horiz * sin(vertical) + Up * cos(vertical);
+   }
+
+#else
+   double C, F, E, N;
+   C = cos(d)*cos(a-a0);
+   F = 1/(sin(d0)*sin(d)+C*cos(d0));
+   N = -F*(cos(d0)*sin(d) - C*sin(d0));
+   E = -F*cos(d)*sin(a-a0);
+   *x =  E*cos(pa) + N*sin(pa);
+   *y = -E*sin(pa) + N*cos(pa);
+#endif
+   return(0);
+}
+
+/* Convert tangent plane (x,y) at (a0,d0,pa) to RA,Dec (a,d) [radians] */
+/* y is rotated CCW by pa from N; NOTE: x is west when PA = 0 (pos parity) */
+int psc_tplonglat(double x, double y, double a0, double d0, double pa,
+	  double density, double zenith, double vertical, 
+	  double *a, double *d)
+{
+#ifndef IRAS
+   double px, py, pz, E, N, pi=4*atan(1.0), dx, dy, z, sec2rad=atan(1.0)/(45*3600);
+   double Horiz, Up;
+/* Undo differential refraction? */
+   if(density > 0) {
+/* Rotate to vertical */
+      Horiz =  x * cos(vertical) + y * sin(vertical);
+      Up = -x * sin(vertical) + y * cos(vertical);
+      z = sqrt(Horiz*Horiz+(zenith-Up)*(zenith-Up));
+      dx = -density*sec2rad*PSC_REFRACT_CONST*tan(z)*Horiz/z;
+      dy = density*sec2rad*PSC_REFRACT_CONST*(tan(z)*(zenith-Up)/z - tan(zenith));
+      Horiz -= dx;
+      Up -= dy;
+/* Rotate back */
+      x =  Horiz * cos(vertical) - Up * sin(vertical);
+      y =  Horiz * sin(vertical) + Up * cos(vertical);
+   }
+   E = -x*cos(pa) + y*sin(pa);
+   N =  x*sin(pa) + y*cos(pa);
+   px = cos(a0)*cos(d0) - E*sin(a0) - N*cos(a0)*sin(d0);
+   py = sin(a0)*cos(d0) + E*cos(a0) - N*sin(a0)*sin(d0);
+   pz = sin(d0) + N*cos(d0);
+   *a = atan2(py, px);
+   if(pz >= 1)       *d =  pi/2;
+   else if(pz <= -1) *d = -pi/2;
+   else              *d = asin(pz);
+#else
+   double DD, B, XX, YY, E, N;
+   E = -x*cos(pa) + y*sin(pa);
+   N =  x*sin(pa) + y*cos(pa);
+   DD = atan(sqrt(x*x+y*y));
+   B = atan2(-x, y);
+   XX = sin(d0)*sin(DD)*cos(B) + cos(d0)*cos(DD);
+   YY = sin(DD)*sin(B);
+   *a = a0 + atan2(YY, XX);
+   *d = asin(sin(d0)*cos(DD)-cos(d0)*sin(DD)*cos(B));
+#endif
+   return(0);
+}
+
+#ifdef TEST_MAIN
+static
+void bomb(char *msg)
+{
+   fprintf(stderr, "%s", msg);
+   exit(1);
+}
+
+static
+void syntax(char *prog)
+{
+   printf("Syntax: %s [options] < input_coords > output coords\n", prog);
+   printf("\nOptions:\n");
+   printf("  in=[sky | tp | fp | ota | cell]     Select input coord type\n");
+   printf("  out=[sky | tp | fp | ota | cell]    Select output coord type\n");
+   printf("\nRequired extra arguments:\n");
+   printf("  sky:   requires ra=R dec=D pa=P for boresight [deg]\n");
+   printf("  tp-fp: requires dx=X dy=Y [\"] dpa=P [deg] sc=S [um/\"] [d3=D [\"^-2]] for optical model [deg]\n");
+   printf("\nOptional extra arguments:\n");
+   printf("  offset=[t|f]    Apply OTA/FP offsets (default f)\n");
+   printf("\nInputs and outputs:\n");
+   printf("  sky:   RA, Dec [deg]\n");
+   printf("  tp:    x,y rotated by PA from N [arcsec]\n");
+   printf("  fp:    xfp,yfp [um] (aka camera coords)\n");
+   printf("  ota:   OTAxy ID and xota,yota [pix]\n");
+   printf("  cell:  OTAxy Cellxy IDs and xcell,ycell [pix]\n");
+   printf("   (OTAxy can be omitted for OTA-Cell conversions only\n");
+   printf("\nSynonyms:\n");
+   printf("  ra=R    or    -ra R     Boresight pointing (deg)\n");
+   printf("  dec=D   or    -dec D    Boresight pointing (deg)\n");
+   printf("  pa=P    or    -pa P     PA of image on sky (deg)\n");
+   printf("  dx=X    or    -dx X     FP offset wrt boresight (arcsec)\n");
+   printf("  dy=Y    or    -dy Y     FP offset wrt boresight (arcsec)\n");
+   printf("  dpa=P   or    -dpa P    FP rotation wrt PA (deg)\n");
+   printf("  sc=S    or    -sc S     Plate scale (um/arcsec)\n");
+   printf("  d3=D    or    -d3 D     Cubic distortiont (arcsec^-2)\n");
+   printf("  offset=t         or    -dooff    Apply OTA offsets? (def=f)\n");
+   printf("  offset=f         or    -nooff    Apply OTA offsets? (def=f)\n");
+   printf("  in=sky out=tp    or    -skytp\n");
+   printf("  in=tp out=fp     or    -tpfp\n");
+   printf("  in=fp out=ota    or    -fpota\n");
+   printf("  in=cell out=ota  or    -clota\n");
+   printf("  in=ota out=fp    or    -otafp\n");
+   printf("\nTo implement differential refraction in Sky-TP:\n");
+   printf("  alt=A   or    -alt A     Altitude (deg)\n");
+   printf("  dens=D  or    -dens D    P/T (wrt STP, 0.71 for HK)\n");
+   printf("  vert=V  or    -vert V    Rotator angle (deg)\n");
+   printf("\nExamples:\n");
+   printf(" echo 33 13 277.36 61.79 | pscoords out=sky in=cell ra=105 dec=7 pa=10 dx=0 dy=112 dpa=20 sc=38.856\n");
+   printf(" -> 105.100000   7.100022\n");
+   printf(" echo 105.1 7.1 | pscoords out=cell in=sky ra=105 dec=7 pa=10 dx=0 dy=112 dpa=20 sc=38.856\n");
+   printf(" -> 33 13    277.36     61.79\n");
+   printf("(note slight bug in Sky<->TP projections depending on distance from pole)\n");
+   printf("\nVersion:  %s\n", rcsid);
+
+   exit(0);
+}
+#endif /* TEST_MAIN */
Index: /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/analysis/libpscoords/pscoords.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/analysis/libpscoords/pscoords.h	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/analysis/libpscoords/pscoords.h	(revision 23594)
@@ -0,0 +1,200 @@
+/* Three-quarter-assed pscoords.h until jt writes me a real one */
+#define NINT(x) (x<0?(int)((x)-0.5):(int)((x)+0.5))
+
+/* Silicon layout of OTAs (um) */
+#define PSC_PIXEL     10.0	/* Pixel size of an OTA */
+#define PSC_HCELL   5900.0	/* Horizontal cell size */
+#define PSC_VCELL   5980.0	/* Vertical cell size */
+#define PSC_HSTREET  120.0	/* Horizontal street between cells (HS) */
+#define PSC_VSTREET  180.0	/* Vertical street between cells (VS) */
+#define PSC_LBORDER  442.0	/* Left border (to left-most cell) (LB) */
+#define PSC_RBORDER  418.0	/* Right border (to right-most vertical street) (RB) */
+#define PSC_TBORDER  354.5	/* Top border (to top-most cell) (TB) */
+#define PSC_BBORDER  975.5	/* Bottom border (to lowest horizontal street) (BB) */
+#define PSC_HDIE   49500.0	/* Horizontal size of silicon die */
+#define PSC_VDIE   50130.0	/* Vertical size of silicon die */
+
+/* Mechanical layout of OTAs (um) */
+#define PSC_HMECH  (1.957*25400)	/* Horizontal mech spacing of OTA placement */
+#define PSC_VMECH  (2.025*25400)	/* Vertical mech spacing of OTA placement */
+#define PSC_VMOFF (PSC_VDIE-(1.568+0.375)*25400) /* Vert die offset between sides */
+
+#define PSC_REFRACT_CONST 60.0	/* Standard refraction ("/tanz) at STP */
+
+/* Number of OTAs populating focal plane */
+#define PSC_NX	     8  /* Number of OTAs in FP in x */
+#define PSC_NY	     8  /* Number of OTAs in FP in y */
+
+/* Cell layout, looking down on back illuminated cell:
+ *  NOTE: center of pixel 0,0 is physical location 0.5,0.5 pixel;
+ *        cell origin is lower left edge of P0,0.
+ *
+ *                +------------------+
+ *                | P0,597  P589,597 |
+ *                |  ...    ...      |
+ *     Amplifier<<| P0,0    P589,0   |
+ *                +------------------+
+ */
+
+/* OTA layout, looking down on back illuminated CCD:
+ * NOTE:  OTA coordinates increase in x from Cell xy0n to Cell xy7n, y from
+ *          Cell xyn0 to Cell xyny, origin is P0,0 in C00, units are pixels.
+ *        "center" of OTA is the middle of the streets between cell 3 and 4
+ *        therefore center is at (xota, yota) = (2423.0, 2434.0)
+ *        Cell coordinates are flipped wrt OTA coordinates, so the x=589
+ *        pixel is the left most one in OTA coordinates, i.e in OTA
+ *        coordinates a cell looks like:    +---------------+
+ *                                          | 589,597 0,597 |
+ *                                          | ...      ...  |
+ *                                          | 589,0   0,0   |> Amp
+ *                                          +---------------+
+ *    +--------Top----------+
+ *    |         TB          |
+ *    L  C07 VS ... C77 VS  R  Cnn is 590x598 10um pixels
+ *    e   HS         HS     i
+ *    fLB...         ...  RBg
+ *    t  C00 VS ... C70 VS  h
+ *    |   HS         HS     t
+ *    |         BB          |
+ *    +-------Bottom--------+ (amp side)
+ */
+
+/* FP layout, looking down on FP:
+ *
+ * NOTE: edge to edge spacing of dice is HMECH,VMECH;
+ *       RHS vertically offset by VMOFF from LHS
+ *          to align pixel i,597 on LHS with pixel i,0 on RHS;
+ *
+ *       "center" is midpoint: OTA33 Cell00 Pix00 and OTA44 Cell00 Pix00
+ *
+ *       Physical coordinate convention runs right to left in x, in 
+ *       order to comply with the "sky view" convention.
+ *
+ *          +--B--+ +--B--+ +--B--+ (VMOFF)
+ *          |     | |     | |     | +--T--+ +--T--+ +--T--+
+ *          R 17  L R 27  L R 37  L |     | |     | |     |
+ *          |     | |     | |     | L 47  R L 57  R L 67  R
+ *          +--T--+ +--T--+ +--T--+ |     | |     | |     |
+ *  +--B--+ +--B--+ +--B--+ +--B--+ +--B--+ +--B--+ +--B--+
+ *  |     | |     | |     | |     | +--T--+ +--T--+ +--T--+ +--T--+
+ *  R 06  L R 16  L R 26  L R 36  L |     | |     | |     | |     |
+ *  |     | |     | |     | |     | L 46  R L 56  R L 66  R L 76  R
+ *  +--T--+ +--T--+ +--T--+ +--T--+ |     | |     | |     | |     |
+ *  +--B--+ +--B--+ +--B--+ +--B--+ +--B--+ +--B--+ +--B--+ +--B--+
+ *  |     | |     | |     | |     | +--T--+ +--T--+ +--T--+ +--T--+
+ *  R 05  L R 15  L R 25  L R 35  L |     | |     | |     | |     |
+ *  |     | |     | |     | |     | L 45  R L 55  R L 65  R L 75  R
+ *  +--T--+ +--T--+ +--T--+ +--T--+ |     | |     | |     | |     |
+ *  +--B--+ +--B--+ +--B--+ +--B--+ +--B--+ +--B--+ +--B--+ +--B--+
+ *  |     | |     | |     | |     | +--T--+ +--T--+ +--T--+ +--T--+
+ *  R 04  L R 14  L R 24  L R 34  L |     | |     | |     | |     |
+ *  |     | |     | |     | |     | L 44  R L 54  R L 64  R L 74  R
+ *  +--T--+ +--T--+ +--T--+ +--T--+ |     | |     | |     | |     |
+ *  +--B--+ +--B--+ +--B--+ +--B--+X+--B--+ +--B--+ +--B--+ +--B--+
+ *  |     | |     | |     | |     | +--T--+ +--T--+ +--T--+ +--T--+
+ *  R 03  L R 13  L R 23  L R 33  L |     | |     | |     | |     |
+ *  |     | |     | |     | |     | L 43  R L 53  R L 63  R L 73  R
+ *  +--T--+ +--T--+ +--T--+ +--T--+ |     | |     | |     | |     |
+ *  +--B--+ +--B--+ +--B--+ +--B--+ +--B--+ +--B--+ +--B--+ +--B--+
+ *  |     | |     | |     | |     | +--T--+ +--T--+ +--T--+ +--T--+
+ *  R 02  L R 12  L R 22  L R 32  L |     | |     | |     | |     |
+ *  |     | |     | |     | |     | L 42  R L 52  R L 62  R L 72  R
+ *  +--T--+ +--T--+ +--T--+ +--T--+ |     | |     | |     | |     |
+ *  +--B--+ +--B--+ +--B--+ +--B--+ +--B--+ +--B--+ +--B--+ +--B--+
+ *  |     | |     | |     | |     | +--T--+ +--T--+ +--T--+ +--T--+
+ *  R 01  L R 11  L R 21  L R 31  L |     | |     | |     | |     |
+ *  |     | |     | |     | |     | L 41  R L 51  R L 61  R L 71  R
+ *  +--T--+ +--T--+ +--T--+ +--T--+ |     | |     | |     | |     |
+ *          +--B--+ +--B--+ +--B--+ +--B--+ +--B--+ +--B--+ +--B--+
+ *          |     | |     | |     | +--T--+ +--T--+ +--T--+
+ *          R 10  L R 20  L R 30  L |     | |     | |     |           y
+ *          |     | |     | |     | L 40  R L 50  R L 60  R           ^
+ *          +--T--+ +--T--+ +--T--+ |     | |     | |     |           |
+ *                                  +--B--+ +--B--+ +--B--+           |
+ *                                                            x  <----+
+ */
+
+/* Structure for tweaks of OTA positions wrt mechanical */
+typedef struct {
+      double dx;	/* OTA x origin found at dx wrt mechanical (um) */
+      double dy;	/* OTA y origin found at dy wrt mechanical (um) */
+      double rot;	/* OTA rotated CCW by rot wrt mechanical (rad) */
+} PSC_OFFROT_T;
+
+/* Overall focal plane offset and rotation (um, rad) */
+const PSC_OFFROT_T psc_fpoff;
+
+/* Offsets and rotations of each OTA (um, millirad) */
+const PSC_OFFROT_T psc_otaoff[PSC_NX*PSC_NY];
+
+/**********************/
+/* Normal Application */
+/**********************/
+/*
+ * To go from a position (a,d) relative to (a0,d0,pa) pointing to cell coords:
+ * ---------------------------------------------------------------------------
+ *   psc_tproject(a, d, a0, d0, pa, density, pi/2-alt, vertical, &x, &y);
+ *   psc_psoptics(x/sec2rad, y/sec2rad, dx, dy, dpa, pscale, d3, &xfp, &yfp);
+ *   psc_fp_to_pixel(xfp, yfp, &ota_xid, &ota_yid, &xota, &yota);
+ *   psc_pixel_to_cell(xota, yota, &cell_xid, &cell_yid, &xcell, &ycell);
+ *
+ * To go from cell coords to a position (a,d) relative to (a0,d0,pa) pointing:
+ * ---------------------------------------------------------------------------
+ *   psc_cell_to_pixel(cell_xid,cell_yid, xcell,ycell, &xota,&yota);
+ *   psc_pixel_to_fp(ota_xid, ota_yid, xota, yota, &xfp, &yfp);
+ *   psc_invoptics(xfp, yfp, dx, dy, dpa, pscale, d3, &x, &y);
+ *   psc_tplonglat(x*sec2rad, y*sec2rad, a0, d0, pa, density,  pi/2-alt, vertical, &a, &d);
+ */
+
+
+/**************/
+/* Prototypes */
+/**************/
+/* Enable application of chip offsets? */
+int psc_do_chipoff(int doit); 	/* Apply chip offsets? (0/1, default=0) */
+
+/* Convert a focal plane position to OTA otax,otay, Cell cellx,celly, Pixel */
+int psc_fp_to_pixel(double xfp_um, double yfp_um, 	/* FP position (um) */
+		int *ota_xid, int *ota_yid,		/* OTA ID (0:7) */
+		double *xota_pix, double *yota_pix);	/* OTA position (pix) */
+
+/* Convert a pixel position in OTA ota_xid,ota_yid to FP */
+int psc_pixel_to_fp(int ota_xid, int ota_yid,		/* OTA ID (0:7) */
+		double xota_pix, double yota_pix,	/* OTA position (pix) */
+		double *xfp_um, double *yfp_um);	/* FP position (um) */
+
+/* Convert an OTA pixel position to cell ID and cell coords */
+int psc_pixel_to_cell(double xota_pix, double yota_pix,	/* OTA position (pix) */
+		  int *cell_xid, int *cell_yid,		/* Cell ID (0:7) or -1 */
+		  double *xcell_pix, double *ycell_pix);/* Cell position (pix) */
+
+/* Convert a cell position and ID to OTA coords */
+int psc_cell_to_pixel(int cell_xid, int cell_yid,	/* Cell ID (0:7) */
+		  double xcell_pix, double ycell_pix,	/* Cell position (pix) */
+		  double *xota_pix, double *yota_pix);	/* OTA position (pix) */
+
+/* Convert tangent plane (x,y) at (a0,d0,pa) to RA,Dec (a,d) [radians] */
+/* y is rotated CCW by pa from N; NOTE: x is west when PA = 0 (pos parity) */
+/* Set density=0 to apply no correction for differential refraction, otherwise P/T in STP */
+int psc_tplonglat(double x_rad, double y_rad, double a0_rad, double d0_rad, double pa_rad,
+		  double density/*0.71 for HK*/, double zenith_rad/*pi/2-ALT*/, 
+		  double vertical_rad/*ROT*/, double *a_rad, double *d_rad);
+
+/* Project RA,Dec (a,d) to the tangent plane (x,y) at (a0,d0,pa) [radians] */
+/* y is rotated CCW by pa from N; NOTE: x is west when PA = 0 (pos parity) */
+/* Set density=0 to apply no correction for differential refraction, otherwise P/T in STP */
+int psc_tproject(double a_rad, double d_rad, double a0_rad, double d0_rad, double pa_rad,
+		 double density/*0.71 for HK*/, double zenith_rad/*pi/2-ALT*/, 
+		 double vertical_rad/*ROT*/, double *x_rad, double *y_rad);
+
+/* Evaluate an inverse optical model: microns -> arcsec notionally */
+/* Offset, scale with *small* distortion, rotate */
+int psc_invoptics(double xfp_um, double yfp_um, double dx_sec, double dy_sec, double dpa_rad, 
+		  double pscale/*38.86um/sec*/, double d3/*1.49e-10sec^-2*/, 
+		  double *x_sec, double *y_sec);
+
+/* Evaluate an optical model: arcsec -> microns notionally */
+/* Offset, scale with distortion, rotate */
+int psc_psoptics(double x_sec, double y_sec, double dx_sec, double dy_sec, double dpa_rad, 
+		 double pscale/*38.86um/sec*/, double d3/*1.49e-10sec^-2*/, 
+		 double *xfp_um, double *yfp_um);
Index: /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/analysis/libpsf/Make.Common
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/analysis/libpsf/Make.Common	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/analysis/libpsf/Make.Common	(revision 23594)
@@ -0,0 +1,1 @@
+link ../Make.Common
Index: /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/analysis/libpsf/Makefile
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/analysis/libpsf/Makefile	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/analysis/libpsf/Makefile	(revision 23594)
@@ -0,0 +1,13 @@
+include ../Make.Common
+CCWARN+=$(WERROR)
+
+EXTRA_FFLAGS=-fno-automatic
+
+include ../Make.Common
+# Dependencies by Make.Common $Revision: 2.17 $
+
+$(OBJ)/psfmargin.o: psfmargin.c psf.h
+$(OBJ)/psfmoment.o: psfmoment.c psf.h
+$(OBJ)/psfdonut.o: psfdonut.c psf.h
+$(OBJ)/psf2dim.o: psf2dim.f psf.h
+$(OBJ)/psf.o: psf.c psf.h
Index: /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/analysis/libpsf/psf.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/analysis/libpsf/psf.c	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/analysis/libpsf/psf.c	(revision 23594)
@@ -0,0 +1,228 @@
+/* psf.c: find and fit the brightest star in an image */
+/* 081009 v1.0 John Tonry */
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/file.h>
+#include <string.h>
+#include <math.h>
+#include "psf.h"
+
+#define SNMIN 5.0	/* Minimum S/N to proceed with PSF_MERGE */
+#define FWMIN 1.0	/* Minimum FWHM to proceed with PSF_MERGE */
+#define FWMAX 8.5	/* Maximum FWHM to proceed with PSF_MERGE */
+#define FWMRG 5.5	/* FWHM which is halfway through MARGIN/MOMENT merge */
+#define FWLAP 1.0	/* Transition width of MARGIN/MOMENT merge */
+#define FWCTE 2.0	/* Maximum yfw/xfw ratio to proceed with PSF_MERGE */
+#define EDGE  2.0	/* Minimum edge proximity to proceed with PSF_MERGE */
+
+int psf(int nx,			/* x size of image */
+	int ny,			/* y size of image */
+	unsigned short *data,	/* Data array */
+	PSF_ALGORITHM alg,	/* Choice of PSF algorithm */
+	PSF_IMPARAM *imparam,	/* Extra image parameters */
+	PSF_PARAM *psfout,	/* Results of fit */
+	PSF_EXTRA *psfextra)	/* Extra results from fit */
+{
+   int err=1;
+   int sx=0, sy=0, binx=1, biny=1, bordx=4, bordy=2, extra, fmax, NX=nx;
+   double xu, yu, fwhm, xfwhm, yfwhm, bkgnd, ftot, weight, snr;
+   int fmax2;
+   double xu2, yu2, fwhm2, xfwhm2, yfwhm2, bkgnd2, ftot2, weight2, snr2;
+   double frac, f;
+
+/* Load up parameters (with a few sanity checks!) */
+   if(imparam != NULL) {
+      sx = imparam->sx;
+      sy = imparam->sy;
+      if(imparam->binx > 0) binx = imparam->binx;
+      if(imparam->biny > 0) biny = imparam->biny;
+      if(imparam->nodata != 0) {
+	 fprintf(stderr, "*NODATA* normally is 0!\n");
+/* Ignore imparam->nodata != 0 (the default) for now! */
+      }
+      if(imparam->NX > 0) NX = imparam->NX;
+      if(imparam->bordx >= 0) bordx = imparam->bordx;
+      if(imparam->bordy >= 0) bordy = imparam->bordy;
+   }
+
+   switch(alg) {
+      case PSF_MARGIN:
+	 err = psfmargin(sx, sy, binx, biny, bordx, bordy,
+			 nx, ny, NX, data, &xu, &yu, &fmax, &fwhm,
+			 &xfwhm, &yfwhm, &bkgnd, &ftot, &weight, &snr);
+	 break;
+
+      case PSF_MOMENT:
+	 err = psfmoment(sx, sy, binx, biny, bordx, bordy,
+			 nx, ny, NX, data, &xu, &yu, &fmax, &fwhm,
+			 &xfwhm, &yfwhm, &bkgnd, &ftot, &weight, &snr, &extra);
+	 break;
+
+      case PSF_BIN:
+	 err = psfdonut(sx, sy, binx, biny, bordx, bordy,
+			 nx, ny, NX, data, &xu, &yu, &fmax, &fwhm,
+			 &xfwhm, &yfwhm, &bkgnd, &ftot, &weight, &snr, &extra);
+	 break;
+
+      case PSF_2DIM:
+	 err = psf2dim(nx, ny, data, imparam, psfout, psfextra);
+	 break;
+
+      case PSF_MERGE:
+	 err = psfmoment(sx, sy, binx, biny, bordx, bordy,
+			 nx, ny, NX, data, &xu, &yu, &fmax, &fwhm,
+			 &xfwhm, &yfwhm, &bkgnd, &ftot, &weight, &snr, &extra);
+/* Do we proceed with psfmargin? */
+	 if(!err && snr > SNMIN && 
+	    fwhm > FWMIN && fwhm < FWMAX && xfwhm < yfwhm*FWCTE &&
+	    xu > sx+EDGE && xu < sx+nx*binx-EDGE && 
+	    yu > sy+EDGE && yu < sy+ny*biny-EDGE) {
+	    err = psfmargin(sx, sy, binx, biny, bordx, bordy,
+			    nx, ny, NX, data, &xu2, &yu2, &fmax2, &fwhm2,
+			    &xfwhm2, &yfwhm2, &bkgnd2, &ftot2, &weight2, &snr2);
+/* Merge the results: frac = fraction of MOMENT to use */
+	    if(!err) {
+/* Control variable f is a linear combination of MAR/MOM */
+	       if(fwhm > FWMRG+FWLAP)      f = fwhm;
+	       else if(fwhm < FWMRG-FWLAP) f = fwhm2;
+	       else f = (fwhm-FWMRG+FWLAP)/(2*FWLAP)*(fwhm-fwhm2)+fwhm2;
+	       f = (f-FWMRG)/(2*FWLAP);
+	       if(f >= 1.0) 	     frac = 1.0;
+	       else if(f <= -1.0)    frac = 0.0;
+	       else 		     frac = 0.5*(sin(f*1.5708)+1);
+	       xu = (1-frac)*xu2 + frac*xu;
+	       yu = (1-frac)*yu2 + frac*yu;
+	       fwhm = (1-frac)*fwhm2 + frac*fwhm;
+	       xfwhm = (1-frac)*xfwhm2 + frac*xfwhm;
+	       yfwhm = (1-frac)*yfwhm2 + frac*yfwhm;
+	       ftot = (1-frac)*ftot2 + frac*ftot;
+	       snr = (1-frac)*snr2 + frac*snr;
+	       weight = (1-frac)*weight2 + frac*weight;
+	    }
+	 }
+
+	 break;
+
+      default:
+	 fprintf(stderr, "Unknown algorithm! %d\n", alg);
+	 return(-1);
+   }
+
+/* Load up results (with a few sanity checks!) */
+   if(psfout != NULL && alg != PSF_2DIM) {
+      psfout->ix = 0;
+      psfout->iy = 0;
+      psfout->x0 = xu;
+      psfout->y0 = yu;
+      psfout->peak = fmax;
+      psfout->fwhm = fwhm;
+      psfout->bkgnd = bkgnd;
+      psfout->flux = ftot;
+      psfout->sn = snr;
+      psfout->weight = err ? 0.0 : snr;
+   }
+
+/* Load up results (with a few sanity checks!) */
+   if(psfextra != NULL) {
+      if(alg == PSF_BIN || alg == PSF_MOMENT || alg == PSF_MARGIN) {
+	 psfextra->xfw = xfwhm;
+	 psfextra->yfw = yfwhm;
+      }
+      if(alg == PSF_BIN) psfextra->binfactor = extra;
+   }
+   return(err);
+}
+
+
+static float *jtpsfbuf=NULL;
+static int jtpsfnpix=0;
+
+int psf2dim(int nx, int ny, unsigned short *data,
+	PSF_IMPARAM *imparam,	/* Extra image parameters */
+	PSF_PARAM *psfout,	/* Results of fit */
+	PSF_EXTRA *psfextra)	/* Extra results from fit */
+{
+   int err;
+   int ix, iy, big;
+   int sx=0, sy=0, binx=1, biny=1;
+   double x0, y0, fwhm, xfwhm, yfwhm, sn, sky, ftot;
+   int i, j, NX=nx;
+   int aprad, skyrad, nwpar;
+   float wpar[16], flux[5], eadu=1.0;
+
+   if(imparam != NULL) {
+      sx = imparam->sx;
+      sy = imparam->sy;
+      if(imparam->binx > 0) binx = imparam->binx;
+      if(imparam->biny > 0) biny = imparam->biny;
+      if(imparam->NX > 0) NX = imparam->NX;
+   }
+
+/* First run psfmargin_guts to get defaults for jtpsf */
+   err = psfmargin_guts(1, &ix, &iy, &big, &x0, &y0, &fwhm, &xfwhm, &yfwhm,
+			&sn, &sky, &ftot, nx, ny, NX, data);
+   if(err) return(err);
+
+/* Make sure that we have some space allocated for the image */
+   if(jtpsfnpix < nx*ny) {
+      if(jtpsfbuf != NULL) free(jtpsfbuf);
+      jtpsfbuf = (float *)calloc(nx*ny, sizeof(float));
+      jtpsfnpix = nx*ny;
+   }
+
+/* Copy the array */
+   for(j=0; j<ny; j++) {
+      for(i=0; i<nx; i++) jtpsfbuf[i+j*nx] = data[i+j*NX];
+   }
+
+/* Parameters to run jtpsf_() */
+   nwpar = 7;
+   wpar[0] = ix;
+   wpar[1] = iy;
+   aprad = 5*fwhm;
+   skyrad = MIN(0.4*nx, 0.4*ny);
+   skyrad = MIN(skyrad, 10*fwhm);
+//   skyrad = MIN(skyrad, 60);
+//   aprad = 32;
+//   skyrad = 60;
+
+//   fprintf(stderr, "nx, ny, aprad, skyrad, nwpar, wpar[0], wpar[1] = %d %d %d %d %d %.2f %.2f\n",
+//	  nx, ny, aprad, skyrad, nwpar, wpar[0], wpar[1]);
+
+   jtpsf_(&nx, &ny, jtpsfbuf, &eadu, &aprad, &skyrad, &nwpar, wpar, flux, &err);
+/* Load up results (with a few sanity checks!) */
+   if(psfout != NULL) {
+      psfout->ix = ix;
+      psfout->iy = iy;
+      psfout->x0 = sx + wpar[0]*binx;
+      psfout->y0 = sy + wpar[1]*biny;
+      psfout->peak = big;
+      psfout->fwhm = sqrt(MAX(0.0, wpar[9]*wpar[10]*binx*biny));
+      psfout->bkgnd = flux[0];
+      psfout->flux = flux[2];
+      psfout->sn = flux[2] / MAX(0.0, flux[3]);
+      psfout->weight = err ? 0.0 : psfout->sn;
+   }
+
+/* Load up extras */
+   if(psfextra != NULL) {
+/* Note that these major/minor things are messed up if binx!=biny */
+      psfextra->majfw = wpar[9]*binx;
+      psfextra->minfw = wpar[10]*biny;
+      psfextra->thfw = wpar[11];
+      psfextra->wpeak = wpar[2];
+      psfextra->wbkgnd = wpar[3];
+      psfextra->dflux = flux[3];
+      psfextra->dbkgnd = flux[1];
+      psfextra->rmsbkgnd = flux[4];
+   }
+   return(err);
+}
+
+void f77msg_(char *line, int len)
+{
+   int i;
+   for(i=len-2; i>0 && line[i] == ' '; i--);
+   line[i+1] = '\0';
+   fprintf(stderr, "f77: %s\n", line);
+}
Index: /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/analysis/libpsf/psf.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/analysis/libpsf/psf.h	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/analysis/libpsf/psf.h	(revision 23594)
@@ -0,0 +1,221 @@
+/*
+ * There are four different algorithms here with different strengths.
+ *
+ * PSF_MARGIN finds the brightest spot, estimates a crude FWHM,
+ *   sums up a marginal profile in x and y, fits a 1D curve to it
+ *   (default Gaussian or parabola), and then sums up the total
+ *   flux with varying sophistication in estimating sky
+ *
+ * PSF_MOMENT estimates sky from the median and then simply computes
+ *   moments of the brightest pixels to ascertain center and FWHM.  It
+ *   iterates once using all pixels within an aperture from the first pass.
+ *   PSF_MOMENT's FWHM is actually 2.35 * the RMS, which is neither the 
+ *   FWHM of a condensed PSF nor the outer diameter of a donut.  It is 
+ *   monotonic in PSF size, so is very appropriate for focussing, especially
+ *   grossly out of focus donuts.
+ *
+ * PSF_2DIM is a rather involved (fortran) routine to fit a full 2-D
+ *   profile at an initial position.  The center, FWHM, position angle,
+ *   and flux it returns are very refined.  The psf() wrapper limits
+ *   some of the underlying features (such as floating data and some
+ *   of the fit quality diagnostics).
+ *
+ * PSF_BIN tries to cope with grossly out of focus data by sucessively
+ *   binning by factors of 2, fitting the profile by PSF_MOMENT.  The
+ *   results are scored and a best-guess combination is returned.
+ *
+ *       Ratings: E-G-F-P for excellent - good - fair - poor
+ *
+ *                        PSF_MARGIN  PSF_MOMENT  PSF_2DIM  PSF_BIN
+ *  Speed                     E           E           F        G
+ *  Overall robustness        E           G           F        G
+ *  Centroid accuracy         E           G           E        G
+ *  FWHM accuracy             G           F*          E        F
+ *  FWHM of donut             P           E           P        G
+ *  Flux/bkgnd accuracy       F           F           E        F
+ *  Extra FWHM info           F           G           E        F
+ *
+ *  PSF_MERGE runs PSF_MOMENT and then if the FWHM is small enough
+ *  it also runs PSF_MARGIN.  It merges the x,y,FWHM from both routines
+ *  in a smooth way as a function of FWHM; preferring MARGIN for small
+ *  FWHM and MOMENT for large.  Obviously it usually costs the CPU time
+ *  for running both routines.
+ */
+
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+#define MAX(a,b) (((a) > (b)) ? (a) : (b))
+#define ABS(a) (((a) > 0) ? (a) : -(a))
+
+/* Enumeration of PSF algorithm choices */
+typedef enum {
+   PSF_MERGE,			/* Merge results from moment and margin */
+   PSF_MARGIN,			/* 1D fit to x,y marginalized profiles */
+   PSF_MOMENT,			/* Moment estimates of center, FWHM */
+   PSF_BIN,			/* Iterative binning, the PSF_MARGIN */
+   PSF_2DIM			/* Full 2D fit of profile */
+} PSF_ALGORITHM;
+
+/* Structure describing extra PSF image parameters */
+typedef struct {
+         int sx; 		/* x offset:  x_real = sx + binx * x_array */
+	 int sy;  		/* y offset:  y_real = sy + biny * y_array */
+	 int binx;  		/* x bin factor */
+	 int biny;   		/* y bin factor */
+	 int nodata;   		/* data value meaning *NO DATA* */
+	 int NX;		/* stride of image storage: addr = [x+NX*y] */
+	 int bordx;  		/* avoidance border width in x */
+	 int bordy;   		/* avoidance border width in y */
+} PSF_IMPARAM;
+
+/* Structure describing output from psf() */
+typedef struct {
+         int ix;		/* Highest pixel x posn in array */
+	 int iy;		/* Highest pixel y posn in array */
+	 double x0;		/* Fitted location of centroid, x */
+	 double y0;		/* Fitted location of centroid, y */
+	 double fwhm;		/* Star fwhm */
+	 double peak;		/* Highest pixel (minus background) */
+	 double bkgnd;		/* Background level */
+	 double flux;		/* Total flux in star */
+	 double sn;		/* Flux signal to noise */
+	 double weight;		/* Overall weight of star: */
+	    			/*    1-10 so-so, 10-30 OK, >30 fine */
+} PSF_PARAM;
+
+/* Structure describing possible extra output from psf() */
+typedef struct {
+	 int binfactor;		/* Best bin factor used for PSF_BIN */
+         double xfw;		/* Star fwhm in x dir */
+	 double yfw;		/* Star fwhm in y dir */
+/* Results available only from PSF_2DIM */
+         double majfw;		/* Major axis fwhm */
+	 double minfw;		/* Minor axis fwhm */
+	 double thfw;		/* Angle of major axis (CCW from x) [rad] */
+	 double wpeak;		/* Peak of Waussian fit */
+	 double wbkgnd;		/* Background of Waussian fit */
+	 double dflux;		/* Uncertainty in total flux estimate */
+	 double dbkgnd;		/* Uncertainty in total background estimate */
+	 double rmsbkgnd;	/* RMS in background */
+} PSF_EXTRA;
+
+int psf(
+   int nx,			/* x size of image */
+   int ny,			/* y size of image */
+   unsigned short *im,		/* Data array */
+   PSF_ALGORITHM alg,		/* Choice of PSF algorithm */
+   PSF_IMPARAM *imparam,	/* Extra image parameters */
+   PSF_PARAM *psf,		/* Results of fit */
+   PSF_EXTRA *extra);		/* Extra results from fit */
+
+/* Prototypes */
+
+/* Basic PSF calculation routine */
+int psfmargin_guts(int algo,	/* What algorithm to use? */
+    int *ix,		/* Highest pixel x posn*/
+    int *iy,		/* Highest pixel y posn */
+    int *big,		/* Highest pixel */
+    double *x0,		/* Fitted location of centroid, x */
+    double *y0,		/* Fitted location of centroid, y */
+    double *fwhm,	/* Star fwhm */
+    double *xfwhm,	/* Star fwhm in x dir */
+    double *yfwhm,	/* Star fwhm in y dir */
+    double *sn,		/* Star signal to noise */
+    double *sky,	/* Sky level */
+    double *flux,	/* Total flux in star */
+    int nx,		/* x size of image */
+    int ny,		/* y size of image */
+    int mx,		/* x size of image storage */
+    unsigned short *im);	/* Data array */
+
+/* Find and report Gaussian fit to position of brightest star */
+int psfmargin(int sx, 			/* x offset of pixel 0,0 */
+	     int sy,  			/* y offset of pixel 0,0 */
+	     int binx,  		/* x bin factor */
+	     int biny,   		/* y bin factor */
+	     int bordx,  		/* x border */
+	     int bordy,   		/* y border */
+	     int nx,   			/* x size of image */
+	     int ny,    		/* y size of image */
+	     int NX,   			/* x stride of image */
+	     unsigned short *data,	/* ushort image data: 0 = *NO_DATA* */
+	     double *xu,		/* Fitted x position */
+	     double *yu,		/* Fitted y position */
+	     int *fmax,			/* Highest pixel in best bin */
+	     double *fwhm,		/* FWHM of psf (binx,y corrected) */
+	     double *xfwhm,		/* FWHM in x dir (binx corrected) */
+	     double *yfwhm,		/* FWHM in y dir (biny corrected) */
+	     double *bkgnd,		/* Sky level */
+	     double *ftot,		/* Total flux */
+	      double *weight,		/* Composite quality of star: <1 bad,*/
+	     				/*    1-10 so-so, 10-30 OK, >30 fine */
+	     double *snr);		/* S/N of (big-sky)/noise */
+
+/* Find and report moments of brightest star */
+int psfmoment(int sx, 			/* x offset of pixel 0,0 */
+	     int sy,  			/* y offset of pixel 0,0 */
+	     int binx,  		/* x bin factor */
+	     int biny,   		/* y bin factor */
+	     int bordx,  		/* x border */
+	     int bordy,   		/* y border */
+	     int nx,   			/* x size of image */
+	     int ny,    		/* y size of image */
+	     int NX,   			/* x stride of image */
+	     unsigned short *data,	/* ushort image data: 0 = *NO_DATA* */
+	     double *xu,		/* Fitted x position */
+	     double *yu,		/* Fitted y position */
+	     int *fmax,			/* Highest pixel in best bin */
+	     double *fwhm,		/* FWHM of psf (binx,y corrected) */
+	     double *xfwhm,		/* FWHM in x dir (binx corrected) */
+	     double *yfwhm,		/* FWHM in y dir (biny corrected) */
+	     double *bkgnd,		/* Sky level */
+	     double *ftot,		/* Total flux */
+	     double *weight,		/* Composite quality of star: <1 bad,*/
+	     				/*    1-10 so-so, 10-30 OK, >30 fine */
+	     double *snr,		/* S/N of (big-sky)/noise */
+	     int *extra);		/* More return stuff? */
+
+/* Find and report on brightest star via hierarchical binning */
+int psfdonut(int sx, 			/* x offset of pixel 0,0 */
+	     int sy,  			/* y offset of pixel 0,0 */
+	     int binx,  		/* x anamorphic compression factor */
+	     int biny,   		/* y anamorphic compression factor */
+	     int bordx,  		/* x border */
+	     int bordy,   		/* y border */
+	     int nx,   			/* x size of image */
+	     int ny,    		/* y size of image */
+	     int NX,   			/* x stride of image */
+	     unsigned short *data,	/* ushort image data: 0 = *NO_DATA* */
+	     double *xu,		/* Fitted x position */
+	     double *yu,		/* Fitted y position */
+	     int *fmax,			/* Highest pixel in best bin */
+	     double *fwhm,		/* FWHM of psf (binx,y corrected) */
+	     double *xfwhm,		/* FWHM in x dir (binx corrected) */
+	     double *yfwhm,		/* FWHM in y dir (biny corrected) */
+	     double *bkgnd,		/* Sky level */
+	     double *ftot,		/* Total flux */
+	     double *weight,		/* Composite quality of star: <1 bad,*/
+	     				/*    1-10 so-so, 10-30 OK, >30 fine */
+	     double *snr,		/* S/N of (big-sky)/noise */
+	     int *binfactor);		/* Best bin factor used */
+
+/* Find and report about brightest star via 2-D fit */
+int psf2dim(
+   int nx,   			/* x size of image */
+   int ny,    			/* y size of image */
+   unsigned short *data,	/* ushort image data: 0 = *NO_DATA* */
+   PSF_IMPARAM *imparam,	/* Extra image parameters */
+   PSF_PARAM *psfout,		/* Results of fit */
+   PSF_EXTRA *psfextra);	/* Extra results from fit */
+
+/* The fortran guts, bleah! */
+int jtpsf_(
+   int *nx,		/* x size */
+   int *ny,		/* y size */
+   float *data,		/* image */
+   float *eadu,		/* e/ADU for noise calc (use 1.0) */
+   int *aprad,		/* Aperture radius */
+   int *skyrad,		/* Sky radius */
+   int *nwpar,		/* Number of fit params (use 7) */
+   float *wpar,		/* Fit results (16) */
+   float *flux,		/* Flux results (5) */
+   int *err);		/* Error */
Index: /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/analysis/libpsf/psf2dim.f
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/analysis/libpsf/psf2dim.f	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/analysis/libpsf/psf2dim.f	(revision 23594)
@@ -0,0 +1,1297 @@
+      subroutine jtpsf(nx, ny, a, eadu, iaprad, iskyrad, 
+     $     nwpar, wpar, flux, ierr)
+* Data array A(NX,NY)
+* Noise scale EADU (i.e. variance = EADU * Sky_level)
+* Radius for aperture flux = IAPRAD
+* Radius for sky estimates = ISKYRAD
+* Order of Waussian fit = NWPAR
+* Coords of star = WPAR(1),WPAR(2)
+* Results of Waussian fit = WPAR:
+*   WPAR( 1) = x0 of fit (TK convention!)
+*   WPAR( 2) = y0 of fit
+*   WPAR( 3) = peak of Waussian
+*   WPAR( 4) = sky of Waussian fit
+*   WPAR( 5) = sx2
+*   WPAR( 6) = sxy
+*   WPAR( 7) = sy2
+*   WPAR( 8) = beta4 
+*   WPAR( 9) = beta6
+*   WPAR(10) = major axis fwhm of Waussian fit
+*   WPAR(11) = minor axis fwhm of Waussian fit
+*   WPAR(12) = angle of major axis (CCW from x axis) [rad]
+*   WPAR(13) = chi^2/Ndof of fit
+*   WPAR(14) = rms residual within a radius of 2 sigma
+*   WPAR(15) = average absolute residual within a radius of 2 sigma
+*   WPAR(16) = max residual within a radius of 2 sigma
+* Results of sky and aperture flux fit = FLUX:
+*   FLUX(1) = sky
+*   FLUX(2) = sky uncertainty
+*   FLUX(3) = flux
+*   FLUX(4) = flux uncertainty
+*   FLUX(5) = rms in sky
+* Error code = IERR
+*                   0: no error, 
+*                  -1: off image, 
+*                   1: fit didn't converge,
+*                N*10: N zero'ed pixels within FWHM
+*
+* Function WAUSS(X,Y,WPAR,Z2) provided below for Waussian evaluation at (x,y)
+*
+* v. 1.11 030527 (JT) added zero pixel error flag
+*
+* v. 1.10 030330 (JT) fixed a few bugs, cleaned up the logic a bit, 
+*      added proper calculation of chi^2/N, FLUX(5) is a new argument, BEWARE
+*
+* Initial version 1.0 030301 John Tonry, adapted from JT Vista fondle()
+*
+      parameter (RESIDCUT=2.0)
+      real a(nx,ny)
+      real wpar(16), flux(5)
+      parameter (MAXRAD=64)
+      real pmed(MAXRAD+1), pave(MAXRAD+1), prms(MAXRAD+1)
+      integer npix(MAXRAD+1), ntot(MAXRAD+1)
+      real smooth(MAXRAD+1), profile(MAXRAD+1)
+* 768 = MAXPT (from pixmedave) * sqrt(2) * (1+fudge)
+      real buf(768*MAXRAD)
+      real ring(MAXRAD), area(MAXRAD), avering(MAXRAD), ering(MAXRAD)
+      real waux(10)
+      real*8 cov(9*9)
+      character*1000 DEBUG
+
+      IGAUSSRAD = 2
+      FWHMGAUSS = 2*sqrt(2*alog(2.))
+
+      profile(1) = 0
+
+      ix = nint(wpar(1))
+      iy = nint(wpar(2))
+
+* Abort this star if it's off the image
+      if(ix.lt.1 .or. ix.gt.nx .or. iy.lt.1 .or. iy.gt.ny) then
+         ierr = -1
+         return
+      end if
+
+****************************************************************
+C Find the highest pixel and get some crude information
+      call crudemax(ix,iy,nx,ny,a,mx,my,peak,crudefw,crudeback)
+
+C      WRITE(DEBUG,*) 'MX, MY, PEAK, CRUDEFW, CRUDEBACK',
+C    $     MX, MY, PEAK, CRUDEFW, CRUDEBACK
+C     CALL F77MSG(DEBUG)
+
+C     Get medians and averages at all radii...
+C      mxr = max(16, min(nint(15*crudefw),MAXRAD))
+      mxr = iskyrad
+      if(mxr .le. 0 .or. mxr .gt. MAXRAD) mxr = MAXRAD
+      call pixmedave(mx,my,nx,ny,a,mxr,npix,ntot,pave,pmed,prms,buf)
+
+C     Get an estimated values for SKY and FWHM
+      call estsky(fwhm,mxr+1,pmed,prms,esky,eskyerr,rms)
+
+C      WRITE(DEBUG,*) 'ESKY, ESKYERR, RMS',
+C     $     ESKY, ESKYERR, RMS
+C      CALL F77MSG(DEBUG)
+
+C     Get a fitted value for SKY and FWHM
+      do 5 i = 1,mxr+1
+         smooth(i) = ((3.*fwhm)/max(1,i-1))**3
+ 5    continue
+      m1 = (mxr+1)/2
+      nfit = mxr+1 - m1 + 1
+
+      call fitsky(nfit,pmed(m1),smooth(m1),ampl,amperr,sky,dsky)
+
+C      WRITE(DEBUG,*) 'AMPL, APERR, SKY, DSKY',
+C     $     AMPL, APERR, SKY, DSKY
+C      CALL F77MSG(DEBUG)
+
+C     Add up flux in annuli of width fwhm/2 or 5
+      iwidth = max(5,nint(fwhm/2))
+      nrad = mxr/iwidth
+      do 6 i = 1,nrad
+         area(i) = 0
+         ring(i) = 0
+         ering(i) = 0
+         avering(i) = 0
+ 6    continue
+      do 7 i = 1,mxr+1
+         idx = (i-1) / iwidth + 1
+*     Get the total flux = AVESUM
+         avesum = (pave(i)-sky) * ntot(i)
+         sum = avesum
+*     Make a more robust estimate of total flux from SUM = MEDIAN * N
+         if(ntot(i).gt.60) sum = (pmed(i)-sky) * ntot(i)
+         area(idx) = area(idx) + ntot(i)
+         ring(idx) = ring(idx) + sum
+         avering(idx) = avering(idx) + avesum
+         ering(idx) = ering(idx) + sum + (sky-esky)*ntot(i)
+ 7    continue
+
+* Add up the total flux
+      call pixsum(mx,my,nx,ny,a,iaprad,ntotal,ave,apflux,peak)
+      apflux = apflux - ntotal*sky
+      dflux = apflux/eadu + (ntotal*dsky)**2 + rms*rms*ntotal
+      if(dflux .ge. 0) then
+         dflux = sqrt(dflux)
+      else
+         dflux = -sqrt(-dflux)
+      end if
+      flux(1) = sky
+      flux(2) = dsky
+      flux(3) = apflux
+      flux(4) = dflux
+      flux(5) = rms
+
+C      WRITE(DEBUG,*) 'SKY, DSKY, APFLUX, DFLUX, RMS', 
+C     $     DSKY, APFLUX, DFLUX, RMS, SKY
+C      CALL F77MSG(DEBUG)
+
+
+************************************************************************
+* Now do a Waussian fit to the data using all we know for initial values
+* How many FWHM do we fit?
+      WRAD = 2.5
+* How many FWHM do we use for evaluating chi/N?
+      WCHI = 2.0
+* Guess at extrasky to meet noise seen by estsky
+      if(rms.ne.0 .and. esky.ne.0) then
+         extrasky = eadu*rms*rms - esky
+      else
+* Disable weighting if something's really wrong with the "sky" level and rms
+         extrasky = -1e10
+      end if
+C WRITE(6,*) EADU, RMS, ESKY, EXTRASKY
+
+* What do we want for a center guess?
+      if(nwpar.ge.7) then
+* Use the positive peak...
+         n = max(3,min(31,max(IGAUSSRAD,nint(WRAD*fwhm))))
+         iwxs = max(0,mx-n)
+         iwys = max(0,my-n)
+      else
+         call domajmin(wpar)
+         n = max(3,min(31,max(IGAUSSRAD,
+     $        nint(WRAD*sqrt(abs(wpar(10)*wpar(11)))))))
+         iwxs = max(0, nint(wpar(1))-n)
+         iwys = max(0, nint(wpar(2))-n)
+      end if
+
+
+* WPAR: X0, Y0, P, SKY, SX2, SXY, SY2, B4, B6, FWX, FWY, PHI
+      if(nwpar.ge.7) then
+         wpar(1) = mx
+         wpar(2) = my
+      end if
+* Waussian fit (WPAR(8) = BETA4 = 1, WPAR(9) = BETA6 = 0.5)
+      wpar(8) = 1.0
+      wpar(9) = 0.5
+* WAUX: NXPATCH, NYPATCH, NX, XOFF, YOFF, EADU, EXTRASKY, IGNORE_VALUE, INIT
+      waux(1) = min(n,nx-1-mx) + n + 1
+      waux(2) = min(n,ny-1-my) + n + 1
+      waux(3) = nx
+      waux(4) = iwxs
+      waux(5) = iwys
+      waux(6) = eadu
+      waux(7) = extrasky
+* Bad data value, by convention 0.0 for JT
+      waux(8) = 0.0
+      waux(9) = 1
+* Don't dare leap to all 9 params at once
+      if(nwpar.gt.7) then
+         niter = 20
+         call waussfit(a(iwxs+1,iwys+1),7,wpar,waux,niter,cov,chisq)
+      end if
+      if(nwpar.gt.2) then
+         niter = 20
+         call waussfit(a(iwxs+1,iwys+1),nwpar,wpar,waux,niter,cov,chisq)
+      else
+         call wausstwo(a(iwxs+1,iwys+1),wpar,waux,chisq)
+      end if
+      wpar(13) = chisq
+
+      if(niter.eq.20 .or. chisq.eq.0) then
+C         ierr = 1
+         ierr = niter
+         return
+      end if
+
+      if(chisq.lt.0) then
+         ierr = nint(chisq)
+         return
+      end if
+
+      ierr = 0
+* Improved estimate of chi^2/N
+      if(rms .gt. 0 .and. wpar(10).gt.0 .and. wpar(11).gt.0) then
+         fw = sqrt(wpar(10)*wpar(11))
+         i0 = max(1,  nint(wpar(1)-WCHI*fw+0.5))
+         i1 = min(nx, nint(wpar(1)+WCHI*fw+0.5))
+         j0 = max(1,  nint(wpar(2)-WCHI*fw+0.5))
+         j1 = min(ny, nint(wpar(2)+WCHI*fw+0.5))
+         chisq = 0
+         npt = 0
+         resid = 0
+         nresid = 0
+         biggie = 0
+         absave = 0
+         do 20 j = j0, j1
+            y = j - 0.5
+            do 21 i = i0, i1
+               if(a(i,j) .eq. waux(8)) then
+                  if((i-wpar(1))*(i-wpar(1))+(j-wpar(2))*(j-wpar(2)) 
+     $                 .le. fw*fw) ierr = ierr + 10
+                  goto 21
+               end if
+               x = i - 0.5
+               diff = a(i,j) - (wauss(x, y, wpar, z2) + wpar(4))
+               chisq = chisq + (diff/rms)**2
+               npt = npt + 1
+               if(z2 .lt. RESIDCUT) then
+                  resid = resid + diff**2
+                  nresid = nresid + 1
+                  if(abs(diff) .gt. abs(biggie)) biggie = diff
+                  absave = absave + abs(diff)
+               end if
+ 21         continue
+ 20      continue
+* Replace wpar(13) with this better chi/N
+         wpar(13) = chisq / max(1,npt-nwpar)
+         wpar(14) = sqrt(resid / max(1,nresid))
+         wpar(15) = absave / max(1,nresid)
+         wpar(16) = biggie
+      end if
+
+      return
+      end
+
+* Compute the value of a Waussian fit at (x,y)  [leaving out the sky]
+      function wauss(x, y, wpar, z2)
+      real wpar(9)
+      x0   = wpar(1)
+      y0   = wpar(2)
+      peak = wpar(3)
+      sky  = wpar(4)
+      sx2  = wpar(5)
+      sxy  = wpar(6)
+      sy2  = wpar(7)
+      b4   = wpar(8)
+      b6   = wpar(9)
+      z2 = sx2*(x-x0)*(x-x0)+sxy*(x-x0)*(y-y0)+sy2*(y-y0)*(y-y0)
+      wauss = peak/(1 + z2*(1 + z2*(0.5*b4 + z2*b6/6)))
+      return
+      end
+
+      subroutine crudemax(ix,iy,nx,ny,a,mx,my,peak,fwhm,back)
+C Find from a rough position (IX,IY), the highest point PEAK at (MX,MY),
+C the rough FWHM, and a rough BACKground of the image in the wings of the star.
+      parameter (pi=3.14159265)
+      real a(nx,ny)
+
+C Find the local maximum
+      maxstep = 30
+      mx = ix
+      my = iy
+      peak = a(mx+1,my+1)
+      do 10 nstep = 1,maxstep
+         peak2 = -1e10
+         do 11 j = my-1,my+1
+            if(j.ge.ny.or.j.lt.0) goto 11
+            do 12 i = mx-1,mx+1
+               if(i.ge.nx.or.i.lt.0) goto 12
+               if(a(i+1,j+1).gt.peak2) then
+                  peak2 = a(i+1,j+1)
+                  im = i
+                  jm = j
+               end if
+ 12         continue
+ 11      continue
+         if(peak2.gt.peak) then
+            mx = im
+            my = jm
+            peak = peak2
+         else
+            goto 15
+         end if
+ 10   continue
+ 15   continue
+
+C Run down the profile in the +/-x and y directions to where it turns up
+      back = 0
+      n = 0
+      do 22 k = 0,3
+         idx = nint(cos(k*pi/2))
+         idy = nint(sin(k*pi/2))
+         do 20 l = 1,maxstep
+            i = mx + idx*l
+            j = my + idy*l
+            if(i.ge.nx.or.i.lt.0.or.j.ge.ny.or.j.lt.0) goto 21
+            if(a(i+1,j+1).gt.a(i+1-idx,j+1-idy)) then
+               n = n + 1
+               back = back + a(i+1,j+1)
+               goto 21
+            end if
+ 20      continue
+ 21      continue
+ 22   continue
+      back = back / max(n,1)
+
+C Find the FWHM using this value for BACK
+      fwhm = 0
+      n = 0
+      do 32 k = 0,3
+         idx = nint(cos(k*pi/2))
+         idy = nint(sin(k*pi/2))
+         do 30 l = 1,maxstep
+            i = mx + idx*l
+            j = my + idy*l
+            if(i.ge.nx.or.i.lt.0.or.j.ge.ny.or.j.lt.0) goto 31
+            if(a(i+1,j+1).lt.peak-(peak-back)/2) then
+               n = n + 1
+               fwhm = fwhm + l
+               goto 31
+            end if
+ 30      continue
+ 31      continue
+ 32   continue
+      fwhm = 2 * fwhm / max(n,1) - 1
+
+      return
+      end
+
+      subroutine pixmedave(mx,my,nx,ny,a,ir,np,ntot,ave,med,rms,buf)
+C Collect average and median of all annuli out to a radius IR
+      parameter (maxpt=512)
+      real a(nx,ny)
+      real ave(1), med(1), rms(1), buf(maxpt,1)
+      integer np(1), ntot(1)
+
+      do 10 i = 1,ir+1
+         np(i) = 0
+         ntot(i) = 0
+         ave(i) = 0
+ 10   continue
+
+C Accumulate pixels from the bottom to top on the right and
+C then top to bottom on the left to get the pixels in azimuthal order
+      do 20 jp = 0,2*(2*ir+1)
+         if(jp.lt.(2*ir+1)) then
+            j = jp - ir
+            ix1 = 0
+            ix2 = ir
+         else
+            j = 3*ir + 1 - jp
+            ix1 = -ir
+            ix2 = -1
+         end if
+         do 21 i = ix1,ix2 
+            r = sqrt(float(i*i+j*j))
+            k = nint(r) + 1
+            if(k.gt.ir+1) goto 21
+            ntot(k) = ntot(k) + 1
+            if(j+my.ge.ny.or.j+my.lt.0) goto 21
+            if(i+mx.ge.nx.or.i+mx.lt.0) goto 21
+            pixel = a(i+mx+1,j+my+1)
+            np(k) = np(k) + 1
+            if(np(k).gt.maxpt) then
+C               write(6,*) 'FONDLE (pixmedave): np exceeded maxpt!'
+*               call f77msg('FONDLE (pixmedave): np exceeded maxpt!')
+               np(k) = maxpt
+            end if
+            ave(k) = ave(k) + pixel
+            buf(np(k),k) = pixel
+ 21      continue
+ 20   continue
+
+C Now compute medians.  If there are sufficient points, correct the median
+C for skewness by assessing the rms, and counting all points around
+C points of greater than 3-sigma which themselves exceed 1-sigma, and
+C throwing out those points from the sorted data.
+
+      do 30 k = 1,ir+1
+         ave(k) = ave(k) / max(np(k),1)
+         do 31 i = 1,np(k)
+            buf(i,1) = buf(i,k)
+ 31      continue
+
+         call qsort4(np(k),buf(1,1))
+         amed = 0.5*(buf((np(k)+1)/2,1)+buf((np(k)+2)/2,1))
+         nsig = max(1,nint(0.1587*np(k)))
+         arms = amed - buf(nsig,1)
+
+C Don't do any funny stuff with radii less than 10 pixels... (N ~ 60)
+         if(np(k).le.60) then
+            med(k) = amed
+            rms(k) = arms
+         else
+            trigger = amed + 3*arms
+C            reset = amed + arms
+            reset = amed
+
+
+C Find a spot which is lower than one-sigma
+            n1 = 1
+C 32         if(buf(n1,k).gt.reset .and. n1.lt.np(k))  then
+ 32         if(buf(n1,k).gt.reset)  then
+               n1 = n1 + 1
+               goto 32
+            end if
+
+
+C Now advance around the circle, counting all pixels higher than RESET
+C which are adjacent to a pixel higher than TRIGGER
+            nuke = 0
+C            n2 = n1 + 1
+* But fed to a mod we want to start at n1+1 - 1
+            n2 = n1
+ 33         if(n2-n1.lt.np(k)) then
+               if(buf(mod(n2,np(k))+1,k).gt.trigger) then
+
+C Back up to find out where the pixels higher than RESET began
+                  i = n2 - 1
+ 34               if(buf(mod(i,np(k))+1,k).gt.reset) then
+                     nuke = nuke + 1
+                     i = i - 1
+                     goto 34
+                  end if
+
+C Advance to find out where the pixels higher than RESET end
+ 35               if(buf(mod(n2,np(k))+1,k).gt.reset) then
+                     nuke = nuke + 1
+                     n2 = n2 + 1
+                     goto 35
+                  end if
+
+               else
+                  n2 = n2 + 1
+               end if
+
+               goto 33
+            end if
+
+C Now recompute the median and rms with NUKE pixels removed off the top
+            n = np(k) - nuke
+            med(k) = 0.5*(buf((n+1)/2,1)+buf((n+2)/2,1))
+            rms(k) = med(k) - buf(nint(0.1587*n),1)
+C            WRITE(6,*) K, MED(K), RMS(K)
+
+         end if
+
+ 30   continue
+
+      return
+      end
+
+      subroutine pixsum(mx,my,nx,ny,a,ir,np,ave,total,peak)
+C Collect average and sum of the flux out to a radius IR
+      real a(nx,ny)
+
+      total = 0
+      np = 0
+      peak = 0
+      do 20 j = -ir,ir
+         iy = j + my + 1
+         if(iy.lt.1.or.iy.gt.ny) goto 20
+         do 21 i = -ir,ir
+            ix = i + mx + 1
+            if(ix.lt.1.or.ix.gt.nx) goto 21
+            ir2 = i*i + j*j
+            if(ir2.gt.ir*ir) goto 21
+            np = np + 1
+            total = total + a(ix,iy)
+            peak = amax1(peak,a(ix,iy))
+ 21      continue
+ 20   continue
+      ave = total / max(1,np)
+
+      return
+      end
+
+      subroutine estsky(fwhm,np,profile,prms,sky,err,rms)
+C Get a decent estimate of SKY and RMS from the PROFILE and PRMS, 
+C and then get a decent estimate of the FWHM
+      real profile(np), prms(np), buf(21)
+
+C First get SKY from a median of the last points in PROFILE
+      n = min(np/2,21)
+      do 10 i = 1,n
+         buf(i) = profile(np-i+1)
+ 10   continue
+      call qsort4(n,buf)
+      sky = 0.5*(buf((n+1)/2)+buf((n+2)/2))
+      err = (sky - buf(max(1,(n+1)/6))) / sqrt(float(n))
+
+C Now get FWHM from the estimated SKY and the central intensity
+      minus = +1
+      if(profile(1).lt.sky) minus = -1
+      half = 0.5*(profile(1) + sky)
+      do 11 i = 1,np
+         if(minus*profile(i).le.minus*half) then
+            ir2 = i
+            goto 12
+         end if
+         if(minus*profile(i).gt.minus*half) ir1 = i
+ 11   continue
+ 12   continue
+
+      budge = 0
+      if(profile(ir1).ne.profile(ir2)) 
+     $     budge = (ir2-ir1)*(profile(ir1)-half) /
+     $     (profile(ir1)-profile(ir2))
+      fwhm = 2*(ir1 - 1 + budge)
+
+C Finally get the RMS as the median of the last several rms points
+      do 20 i = 1,n
+         buf(i) = prms(np-i+1)
+ 20   continue
+      call qsort4(n,buf)
+      rms = 0.5*(buf((n+1)/2)+buf((n+2)/2))
+
+      return
+      end
+
+      subroutine fitsky(n,profile,smooth,ampl,amperr,sky,err)
+C Fit the profile as AMPL * SMOOTH + SKY, and return the error as ERR
+      real profile(n), smooth(n)
+      real*8 v(2), am(3), sum, sum2, det
+
+      v(1) = 0
+      v(2) = 0
+      am(1) = 0
+      am(2) = 0
+      am(3) = 0
+
+C Accumulate sums
+      do 20 j = 1,n
+         v(1) = v(1) + profile(j)
+         v(2) = v(2) + profile(j)*smooth(j)
+         am(1) = am(1) + 1
+         am(2) = am(2) + smooth(j)*smooth(j)
+         am(3) = am(3) + smooth(j)
+ 20   continue
+
+      det = am(1)*am(2) - am(3)*am(3)
+      if(det.eq.0) then
+         sky = 0
+         ampl = 0
+         amperr = 0
+         err = 0
+         return
+      end if
+      tmp = am(2)
+      am(2) = am(1) / det
+      am(1) = tmp / det
+      am(3) = -am(3) / det
+
+      sky = am(1)*v(1) + am(3)*v(2)
+      ampl = am(3)*v(1) + am(2)*v(2)
+
+      if(ampl.lt.0) ampl = 0
+
+      sum = 0
+      sum2 = 0
+      do 30 j = 1,n
+         sum = sum + profile(j)-(sky + ampl*smooth(j))
+         sum2 = sum2 + (profile(j)-(sky + ampl*smooth(j)))**2
+ 30   continue
+
+      sum = sum / n
+      rms = dsqrt(dabs(sum2/n-sum*sum))
+
+      if(ampl.eq.0) sky = sky + sum
+
+      err = sqrt(am(1)) * rms
+      amperr = sqrt(am(2)) * rms
+      cov = am(3) / sqrt(am(1)*am(2))
+
+      return
+      end
+
+
+      SUBROUTINE QSORT4(N,X)
+* Sorting program that uses a quicksort algorithm
+* c. 1978 JT
+      parameter (maxstack=256)
+      REAL X(N)
+      REAL KEY, KL, KR, KM, TEMP
+      INTEGER L, R, M, LSTACK(maxstack+1), RSTACK(maxstack+1), SP
+      INTEGER NSTOP
+      LOGICAL MGTL, LGTR, RGTM
+      DATA NSTOP /15/
+
+      IF(N.LE.NSTOP) GOTO 100
+      SP = 0
+      SP = SP + 1
+      LSTACK(SP) = 1
+      RSTACK(SP) = N
+
+* Sort a subrecord off the stack
+* Set KEY = median of X(L), X(M), X(R)
+1     L = LSTACK(SP)
+      R = RSTACK(SP)
+      SP = SP - 1
+      M = (L + R) / 2
+      KL = X(L)
+      KM = X(M)
+      KR = X(R)
+      MGTL = KM .GT. KL
+      RGTM = KR .GT. KM
+      LGTR = KL .GT. KR
+      IF(MGTL .EQV. RGTM) THEN
+          IF(MGTL .EQV. LGTR) THEN
+              KEY = KR
+          ELSE
+              KEY = KL
+          ENDIF
+      ELSE
+          KEY = KM
+      ENDIF
+
+      I = L
+      J = R
+
+* Find a big record on the left
+10    IF(X(I).GE.KEY) GOTO 11
+      I = I + 1
+      GOTO 10
+11    CONTINUE
+* Find a small record on the right
+20    IF(X(J).LE.KEY) GOTO 21
+      J = J - 1
+      GOTO 20
+21    CONTINUE
+      IF(I.GE.J) GOTO 2
+* Exchange records
+      TEMP = X(I)
+      X(I) = X(J)
+      X(J) = TEMP
+      I = I + 1
+      J = J - 1
+      GOTO 10
+
+* Subfile is partitioned into two halves, left .le. right
+* Push the two halves on the stack
+2     IF(J-L+1 .GT. NSTOP) THEN
+          SP = SP + 1
+          LSTACK(SP) = L
+          RSTACK(SP) = J
+      ENDIF
+      IF(R-J .GT. NSTOP) THEN
+          SP = SP + 1
+          LSTACK(SP) = J+1
+          RSTACK(SP) = R
+      ENDIF
+      IF(SP.GT.MAXSTACK) THEN
+C         WRITE(6,*) 'QSORT4: Fatal error from stack overflow'
+C         WRITE(6,*) 'Fall back on sort by insertion'
+*         call f77msg('QSORT4: Fatal error from stack overflow')
+*         call f77msg('Fall back on sort by insertion')
+         GOTO 100
+      END IF
+
+* Anything left to process?
+      IF(SP.GT.0) GOTO 1
+
+* Sorting routine that sorts the N elements of single precision
+* array X by straight insertion between previously sorted numbers
+100   DO 110 J = N-1,1,-1
+      K = J
+      DO 120 I = J+1,N
+      IF(X(J).LE.X(I)) GOTO 121
+120   K = I
+121   CONTINUE
+      IF(K.EQ.J) GOTO 110
+      TEMP = X(J)
+      DO 130 I = J+1,K
+130   X(I-1) = X(I)
+      X(K) = TEMP
+110   CONTINUE
+      RETURN
+      END
+
+      subroutine waussfit(data,npar,par,aux,niter,cov,chisq)
+*     Program to fit a source with a Wingy Gaussian
+*
+*     Input:	data	Pixel array
+*     NPAR      How many parameters to fit: 4, 7, 9
+*               Note: NPAR=9 wants parameters to be first fitted with NPAR=7!
+*     NITER     Max number of iterations requested
+*     PAR(1)    Initial guess for x0 (includes XOFF)
+*     PAR(2)    Initial guess for y0 (includes YOFF)
+*     PAR(8)    BETA4: r^4 coeff of Waussian (< 0 => Fit Gaussian instead)
+*     PAR(9)    BETA6: r^6 coeff of Waussian
+*
+*     AUX(1)    NX	Number of columns in DATA
+*     AUX(2)    NY	Number of rows in DATA
+*     AUX(3)    NXDIM   Column dimension of DATA
+*     AUX(4)    XOFF	X offset of subarray in entire array
+*     AUX(5)    YOFF	Y offset of subarray in entire array
+*     AUX(6)    EPERADU	E/ADU for calculating sigma
+*     AUX(7)    EXTRA	Added value so that variance = E/ADU*(DATA+EXTRA)
+*     AUX(8)    IGNORE	Pixel value to ignore in fitting
+*     AUX(9)    INIT	Initialize params?
+*                       0, start with what's in PAR already
+*                       1, initialize all but x0,y0 = PAR(1,2)
+*                       2, also find peak to start x0,y0 = PAR(1,2)
+*                       
+*      Output:	PAR(1:12)   (8,9 left alone if NPAR <= 7)
+*  1   XC	x center of Gaussian; first pixel is 0 < x < 1
+*  2   YC	y center of Gaussian; first pixel is 0 < y < 1
+*  3   PEAK	Central intensity of Gaussian
+*  4   SKY	Constant added to Gaussian
+*  5   SX2	Squared Gaussian width in the x direction
+*  6   SXY	Cross term of Gaussian width
+*  7   SY2	Squared Gaussian width in the y direction
+*  8   BETA4	r^4 coefficient in Waussian (< 0 => Fit Gaussian instead)
+*  9   BETA6	r^6 coefficient in Waussian
+* 10   FWMAJ	Gaussian FWHM width - major axis
+* 11   FWMIN	Gaussian FWHM width - minor axis
+* 12   PHI      Angle of major axis
+*      NITER 	Number of iterations carried out
+*
+      parameter (pi=3.14159265, gausshalf=2.3548)
+      real*8 cov(npar,npar)
+      real par(12), aux(9)
+      real data(1)
+      character*1024 blabline
+      external weval
+
+      IBLAB = 0
+
+      nx = nint(aux(1))
+      ny = nint(aux(2))
+      nxdim = nint(aux(3))
+      init = nint(aux(9))
+
+* Set up initial values for parameters...
+      if(init.gt.1) then
+* Find the peak for INIT .GE. 2
+         imax = 1
+         jmax = 1
+         do 10 j = 1,ny
+            do 11 i = 1,nx
+               if(data(i+(j-1)*nxdim).gt.data(imax+(jmax-1)*nxdim)) then
+                  imax = i
+                  jmax = j
+               end if
+ 11         continue
+ 10      continue
+         par(1) = imax - 0.5 + aux(4)
+         par(2) = jmax - 0.5 + aux(5)
+      end if
+
+* Internal to waussfit the position is relative to the subarray...
+      par(1) = par(1) - aux(4)
+      par(2) = par(2) - aux(5)
+
+* Continue initialization for INIT .GE. 1
+      if(init.gt.0) then
+         imax = nint(par(1) + 0.5)
+         jmax = nint(par(2) + 0.5)
+
+         par(4) = amin1(data(1), data(1+(ny-1)*nxdim),
+     $        data(nx), data(nx+(ny-1)*nxdim))
+         par(3) = data(imax+(jmax-1)*nxdim) - par(4)
+         half = 0.5*(par(3)-par(4)) + par(4)
+         if(npar.gt.4) then
+            do 12 i = imax+1,nx
+               if(data(i+(jmax-1)*nxdim).lt.half) then
+                  width = i - imax
+                  goto 13
+               end if
+ 12         continue
+            width = nx / 2
+ 13         continue
+            par(5) = 1 / (width/1.2)**2
+            par(6) = 0.001
+            par(7) = par(5)
+         end if
+         if(npar.gt.7) then
+            par(8) = 1
+            par(9) = 1
+         end if
+      end if
+
+      acc = 0.001
+
+      alamb = -1
+      if(iblab.gt.0) then
+C         WRITE(6,*) 'ATTEMPTING FITMRQ', aux(6), aux(7)
+*         WRITE(blabline,*) 'ATTEMPTING FITMRQ', aux(6), aux(7)
+*         call f77msg(blabline)
+      end if
+
+      nvar = 9
+
+      if(iblab.gt.0) then
+C         WRITE(6,1511) -1,-20,0.0,(PAR(J),J=1,7)
+*         WRITE(blabline,1511) -1,-20,0.0,(PAR(J),J=1,7)
+*         call f77msg(blabline)
+      end if
+      chiold = fitmrq(nx*ny,aux,v,data,nvar,npar,par,cov,alamb,weval)
+      if(chiold .lt. 0) return
+      if(iblab.gt.0) then
+C         WRITE(6,1511) 0,nint(alog10(alamb)),CHIOLD,(PAR(J),J=1,7)
+*         WRITE(blabline,1511) 0, nint(alog10(alamb)), CHIOLD,
+*     $        (PAR(J),J=1,7)
+* 1511    FORMAT(2I4,1pe12.4,0p2F9.3,2F9.1,5F9.4)
+*         call f77msg(blabline)
+      end if
+      miter = 0
+      do 20 i = 1,niter
+         chisq = fitmrq(nx*ny,aux,v,data,nvar,npar,par,cov,alamb,weval)
+         if(chisq .lt. 0) return
+         miter = miter + 1
+         if(iblab.gt.0) then
+C            WRITE(6,1511) MITER,nint(alog10(alamb)),CHISQ,(PAR(J),J=1,7)
+*            WRITE(blabline,1511) MITER, nint(alog10(alamb)), CHISQ,
+*     $           (PAR(J),J=1,7)
+*            call f77msg(blabline)
+         end if
+         if(abs(chisq-chiold) .lt. acc*chiold .and.
+     $        alamb.le.0.001) goto 21
+         chiold = amin1(chiold,chisq)
+ 20   continue
+ 21   alamb = 0
+      chisq = fitmrq(nx*ny,aux,v,data,nvar,npar,par,cov,alamb,weval)
+      if(chisq .lt. 0) return
+      if(iblab.gt.0) then
+C         WRITE(6,1511) miter,-20,CHISQ,(PAR(J),J=1,7)
+*         WRITE(blabline,1511) miter,-20,CHISQ,(PAR(J),J=1,7)
+*         call f77msg(blabline)
+      end if
+      niter = miter
+
+* Patch up and fill in the parameters
+      par(1) = par(1) + aux(4)
+      par(2) = par(2) + aux(5)
+
+* Calculate sigma's and position angles from sx2, sxy, sy2
+      call domajmin(par)
+
+C      WRITE(6,*) PAR
+
+      return
+      end
+
+      subroutine wausstwo(data,par,aux,chisq)
+C Fit a Waussian using only PEAK and SKY; he's linear, Jim.
+      real par(12), aux(9)
+      real data(1)
+      real*8 v(2), am(3), det, dat, fit
+
+      nx = nint(aux(1))
+      ny = nint(aux(2))
+      nxdim = nint(aux(3))
+* Internal to waussfit the position is relative to the subarray...
+      x0 = par(1) - aux(4)
+      y0 = par(2) - aux(5)
+      sx2 = par(5)
+      sxy = par(6)
+      sy2 = par(7)
+      b4 = par(8) / 2
+      b6 = par(9) / 6
+
+      v(1) = 0
+      v(2) = 0
+      am(1) = 0
+      am(2) = 0
+      am(3) = 0
+
+      baddata = aux(8)
+
+C Accumulate sums
+      do 10 j = 0,ny-1
+         do 11 i = 0,nx-1
+            x = i + 0.5
+            y = j + 0.5
+            if(data(i+1+j*nxdim) .eq. baddata) goto 11
+            z2 = sx2*(x-x0)*(x-x0)+sxy*(x-x0)*(y-y0)+sy2*(y-y0)*(y-y0)
+            fit = 1 / (1+z2*(1+z2*(b4+z2*b6)))
+            dat = data(i+1+j*nxdim)
+            v(1) = v(1) + dat
+            v(2) = v(2) + dat*fit
+            am(1) = am(1) + 1
+            am(2) = am(2) + fit*fit
+            am(3) = am(3) + fit
+ 11      continue
+ 10   continue
+
+      det = am(1)*am(2) - am(3)*am(3)
+      if(det.eq.0) then
+         par(3) = 0
+         par(4) = 0
+         chisq = -2
+         return
+      end if
+      tmp = am(2)
+      am(2) = am(1) / det
+      am(1) = tmp / det
+      am(3) = -am(3) / det
+
+      par(3) = am(3)*v(1) + am(2)*v(2)
+      par(4) = am(1)*v(1) + am(3)*v(2)
+
+      call domajmin(par)
+      chisq = 1
+      return
+      end
+
+
+      subroutine domajmin(par)
+* Calculate sigma's and position angles from sx2, sxy, sy2
+      parameter (pi=3.14159265, gausshalf=2.3548)
+      real par(12), aux(9)
+
+      if(par(5).eq.par(7)) then
+         par(12) = pi/4
+         par(10) = par(5) + par(7)
+         par(11) = par(10)
+      else
+         par(12) = 0.5*atan(par(6)/(par(5)-par(7)))
+         par(10) = par(5) + par(7) + (par(5)-par(7))/cos(2*par(12))
+         par(11) = par(5) + par(7) - (par(5)-par(7))/cos(2*par(12))
+         if(par(10).gt.par(11)) then
+            tmp = par(10)
+            par(10) = par(11)
+            par(11) = tmp
+            par(12) = par(12) - pi/2
+         end if
+      end if
+      if(par(12).lt.0) par(12) = par(12) + pi
+
+* Take careful square roots
+      if(par(10).gt.0) then
+         par(10) = gausshalf / sqrt(par(10))
+      else if(par(10).eq.0) then
+         par(10) = 0
+      else
+         par(10) = -gausshalf / sqrt(-par(10))
+      end if
+
+      if(par(11).gt.0) then
+         par(11) = gausshalf / sqrt(par(11))
+      else if(par(11).eq.0) then
+         par(11) = 0
+      else
+         par(11) = -gausshalf / sqrt(-par(11))
+      end if
+      return
+      end
+
+      function weval(k,aux,v,data,ydat,wgt, npar,par,yfit,dyda)
+* Evaluate things for fitmrq
+      parameter (half=0.5, sixth=0.1666666666)
+      real data(1)
+      real dyda(npar), par(9), aux(8)
+      character*1024 blabline
+
+C      WRITE(6,*) 'ENTERED WEVAL', 
+C      WRITE(6,*) PAR
+C      WRITE(6,*) AUX
+      x0 = par(1)
+      y0 = par(2)
+      peak = par(3)
+      sky = par(4)
+      sx2 = par(5)
+      sxy = par(6)
+      sy2 = par(7)
+      b4 = par(8)
+      b6 = par(9)
+
+      nx = nint(aux(1))
+      ny = nint(aux(2))
+      nxdim = nint(aux(3))
+      eperadu = aux(6)
+      extrasky = aux(7)
+      baddata = aux(8)
+
+*      WRITE(blabline, *) 'Entering weval', (par(i),i=1,9),(aux(i),i=1,8)
+*      call f77msg(blabline)
+
+      if(nx.eq.0) then
+C         write(6,*) 'WEVAL: zero dimension???'
+         call f77msg('WEVAL: zero dimension???')
+         weval = -1
+         return
+      end if
+      i = mod(k-1,nx)
+      j = (k-1)/nx
+
+C      WEVAL = DATA(NXDIM*J + I + 1)
+C      WRITE(6,*) 'WEVAL COORDS', I, J
+
+      x = i + 0.5
+      y = j + 0.5
+
+      z2 = sx2*(x-x0)*(x-x0) + sxy*(x-x0)*(y-y0) + sy2*(y-y0)*(y-y0)
+      fn = 0
+      if(z2.lt.85) then
+         if(b4.lt.0 .and. npar.le.7) then
+            fn = exp(-z2)
+            dfdz2 = 1/fn
+         else
+            fn = 1/(1 + z2*(1 + z2*(half*b4 + z2*sixth*b6)))
+            dfdz2 = 1 + z2*(b4 + z2*half*b6)
+         end if
+      end if
+
+      yfit = sky + peak*fn
+
+*      WRITE(blabline, *) 'weval:', sx2,sxy,sy2, z2, fn, sky, peak, yfit
+*      call f77msg(blabline)
+
+      dyda(1) = peak*fn*fn*dfdz2*(2*sx2*(x-x0) + sxy*(y-y0))
+      dyda(2) = peak*fn*fn*dfdz2*(sxy*(x-x0) + 2*sy2*(y-y0))
+      dyda(3) = fn
+      dyda(4) = 1
+      if(npar.gt.4) then
+         dyda(5) = -peak*fn*fn*dfdz2*(x-x0)*(x-x0)
+         dyda(6) = -peak*fn*fn*dfdz2*(x-x0)*(y-y0)
+         dyda(7) = -peak*fn*fn*dfdz2*(y-y0)*(y-y0)
+      end if
+      if(npar.gt.7) then
+         dyda(8) = -peak*fn*fn*half*z2*z2
+         dyda(9) = -peak*fn*fn*sixth*z2*z2*z2
+      end if
+
+      weval = 1
+      ydat = data(nxdim*j + i + 1)
+      if(ydat.eq.baddata) then
+         ydat = yfit
+         weval = 0
+      end if
+
+      if(extrasky.gt.-9e9) then
+         wgt = eperadu/(ydat+extrasky)
+         if(wgt.lt.0) wgt = 1
+      else
+         wgt = 1
+      end if
+
+*      WRITE(blabline, *) 'Leaving weval', weval, yfit, wgt, 
+*     $     (par(i),i=1,9), (dyda(i),i=1,9)
+*      call f77msg(blabline)
+
+      return
+      end
+
+      function fitmrq(ndata,u,v,y,nvar,npar,par,cov,alamb,func)
+* Levenberg-Marquardt method, fitting PAR(NPAR) to data Y(NDATA)
+* In order to provide as much flexibility as possible, the arrays
+* (U,V are convenience arrays) are all passed through FUNC:
+*
+*     FUNC(I,U,V,Y,YDAT,WGT, NPAR,PAR,YFIT,DYDA) 
+*
+*        should return the 0/1 for data value used as well as filling in
+*        YDAT, YFIT, DYDA(NPAR), and WGT.
+*
+* Note that NVAR parameters are maintained, but only NPAR are varied.
+*
+* ALAMB < 0 => initialize; otherwise usual LM parameter; 
+* COV = scratch space (returns slightly messed up covariance matrix)
+* A final call with ALAMB = 0 will fill COV with proper covariance matrix
+* FITMRQ returns Chi^2/NDOF
+*
+      parameter (maxpar=20)
+      real*8 cov(npar,npar)
+      real par(nvar), ptry(maxpar)
+      real*8 alpha(maxpar*maxpar), beta(maxpar), dpar(maxpar)
+      external func
+      character*1024 blabline
+
+* Initialize with first value of Chi^2
+      if(alamb .lt. 0) then
+         alamb = 0.001
+         chisq = chimrq(ndata,u,v,y,npar,par,alpha,beta,func)
+         ochisq = chisq
+         fitmrq = chisq
+         do 13 j=1,nvar
+            ptry(j) = par(j)
+ 13      continue
+         return
+      end if
+*      write(blabline, *) 'frmq1: ', chisq, (par(i),i=1,npar)
+*      call f77msg(blabline)
+
+* Build L-M matrix
+      do 15 j=1,npar
+        do 14 k=1,npar
+          cov(j,k) = alpha(k+(j-1)*npar)
+14      continue
+        cov(j,j) = alpha(j+(j-1)*npar)*(1.+alamb)
+        dpar(j) = beta(j)
+15    continue
+
+* Solve for new DPAR
+      ierr = jordangauss(cov,npar,npar,dpar,1,1)
+      if(ierr .ne. 0) then
+         fitmrq = -1
+         return
+      end if
+
+* ALAMB = 0 implies COV is now covariance matrix, so just return
+      if(alamb .eq. 0.0) then
+        fitmrq = chisq
+        return
+      end if
+
+* Evaluate new Chi^2
+      do 16 j=1,npar
+         ptry(j) = par(j) + dpar(j)
+16    continue
+
+C      WRITE(6,*) 'ABOUT TO TRY NEW DATA'
+C      WRITE(6,*) PTRY
+C      WRITE(6,*) DPAR
+
+      chisq = chimrq(ndata,u,v,y,npar,ptry,cov,dpar,func)
+
+*      write(blabline, *) 'frmq2: ', chisq, (ptry(i),i=1,npar)
+*      call f77msg(blabline)
+
+* Test for improvement and adjust L-M parameter
+      if(chisq.lt.ochisq)then
+         alamb = 0.1*alamb
+         ochisq = chisq
+         do 18 j=1,npar
+            do 17 k=1,npar
+               alpha(k+(j-1)*npar) = cov(j,k)
+ 17         continue
+            beta(j) = dpar(j)
+            par(j) = ptry(j)
+ 18      continue
+      else
+         alamb = 10.*alamb
+         chisq = ochisq
+      end if
+      fitmrq = chisq
+      return
+      end
+
+      function chimrq(ndata,u,v,y,npar,par,alpha,beta,func)
+      parameter (maxpar=20)
+      real*8 alpha(npar,npar), beta(npar)
+      real dyda(maxpar)
+      external func
+      character*1024 blabline
+
+* Zero out second derivative matrix and y vector
+      do 12 j=1,npar
+         do 11 k=1,j
+            alpha(j,k) = 0.0
+ 11      continue
+         beta(j) = 0.0
+ 12   continue
+      chimrq=0.0
+
+* Add up sums 
+      dof = -npar
+      do 15 i = 1,ndata
+         used = func(i,u,v,y,ydata,wgt,npar,par,ymod,dyda)
+*         write(blabline, *) i, ymod, used
+*         call f77msg(blabline)
+         dof = dof + used
+         dy = ydata - ymod
+         do 14 j=1,npar
+            wt = dyda(j) * wgt
+            do 13 k=1,j
+               alpha(j,k) = alpha(j,k) + wt*dyda(k)
+ 13         continue
+            beta(j) = beta(j) + dy*wt
+ 14      continue
+         chimrq = chimrq + dy*dy*wgt
+ 15   continue
+* Convert to Chi^2/N
+      chimrq = chimrq / amax1(1.0, dof)
+
+* Symmetrize
+      do 17 j=2,npar
+         do 16 k=1,j-1
+            alpha(k,j) = alpha(j,k)
+ 16      continue
+ 17   continue
+
+      return
+      end
+
+      function jordangauss(a,n,np,b,m,mp)
+      implicit real*8 (a-h,o-z)
+      parameter (nmax=50)
+      real*8 a(np,np), b(np,mp)
+      integer ipiv(nmax), indxr(nmax), indxc(nmax)
+      do 11 j=1,n
+         ipiv(j)=0
+ 11   continue
+      do 22 i=1,n
+         big=0.
+         do 13 j=1,n
+            if(ipiv(j).ne.1)then
+               do 12 k=1,n
+                  if (ipiv(k).eq.0) then
+                     if (abs(a(j,k)).ge.big)then
+                        big=abs(a(j,k))
+                        irow=j
+                        icol=k
+                     endif
+                  else if (ipiv(k).gt.1) then
+C                     write(6,*) 'singular matrix'
+                     jordangauss = -1
+                     return
+                  endif
+ 12            continue
+            endif
+ 13      continue
+         ipiv(icol)=ipiv(icol)+1
+         if (irow.ne.icol) then
+            do 14 l=1,n
+               dum=a(irow,l)
+               a(irow,l)=a(icol,l)
+               a(icol,l)=dum
+ 14         continue
+            do 15 l=1,m
+               dum=b(irow,l)
+               b(irow,l)=b(icol,l)
+               b(icol,l)=dum
+ 15         continue
+         endif
+         indxr(i)=irow
+         indxc(i)=icol
+         if (a(icol,icol).eq.0.) then
+C            write(6,*) 'singular matrix.'
+            jordangauss = -1
+            return
+         end if
+         pivinv=1./a(icol,icol)
+         a(icol,icol)=1.
+         do 16 l=1,n
+            a(icol,l)=a(icol,l)*pivinv
+ 16      continue
+         do 17 l=1,m
+            b(icol,l)=b(icol,l)*pivinv
+ 17      continue
+         do 21 ll=1,n
+            if(ll.ne.icol)then
+               dum=a(ll,icol)
+               a(ll,icol)=0.
+               do 18 l=1,n
+                  a(ll,l)=a(ll,l)-a(icol,l)*dum
+ 18            continue
+               do 19 l=1,m
+                  b(ll,l)=b(ll,l)-b(icol,l)*dum
+ 19            continue
+            endif
+ 21      continue
+ 22   continue
+      do 24 l=n,1,-1
+         if(indxr(l).ne.indxc(l))then
+            do 23 k=1,n
+               dum=a(k,indxr(l))
+               a(k,indxr(l))=a(k,indxc(l))
+               a(k,indxc(l))=dum
+ 23         continue
+         endif
+ 24   continue
+      jordangauss = 0
+      return
+      end
Index: /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/analysis/libpsf/psfdonut.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/analysis/libpsf/psfdonut.c	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/analysis/libpsf/psfdonut.c	(revision 23594)
@@ -0,0 +1,184 @@
+/* psfdonut.c: find and fit the brightest star in an image; donuts OK */
+
+/* Note: this is not as fast as psf() nor as accurate as jtpsf() for
+ * flux, width, and position, but it is tolerant of donuts and should
+ * provide a pretty good width and center.
+ */
+/* 080815 v1.0 John Tonry */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/file.h>
+#include <math.h>
+#include "psf.h"
+
+#define MAX(a,b) (((a) > (b)) ? (a) : (b))
+
+static unsigned short int *binim=NULL;	/* Storaged for binned images */
+static int binsize=0;			/* How many pixels? */
+#define LOGMAXBIN 4	/* Bin down to 2^LOGMAXBIN, 32=2^5 is a bit much... */
+// #define XBORD 2		/* Disregard this border on the x sides */
+// #define YBORD 1		/* Disregard this border on the y sides */
+
+// #define DONUT_TEST		/* Test output for donut routine? */
+
+/* Find and report on the brightest star; will work with donuts... */
+int psfdonut(int ZFsx, 			/* x offset of pixel 0,0 */
+	     int ZFsy,  		/* y offset of pixel 0,0 */
+	     int ZFbinx,  		/* x anamorphic compression factor */
+	     int ZFbiny,   		/* y anamorphic compression factor */
+/* Note: "anamorphic compression" means that the data received have been
+ * binned down by that factor.  We *do* correct the positions xu,yu
+ * for this factor, and *do* correct the widths for it.  The reasoning, such
+ * as it is, returns unbinned positions, correct for offsets, and
+ * the widths need to be corrected here because roundness is such an important
+ * factor in deciding whether we've got a good fit or not.  Ugh.
+ */
+	     int bordx,  		/* x border */
+	     int bordy,   		/* y border */
+	     int ZFnx,   		/* x size of image */
+	     int ZFny,    		/* y size of image */
+	     int ZFNX,   		/* x stride of image */
+	     unsigned short *ZFdata,	/* ushort image data: 0 = *NO_DATA* */
+	     double *xu,		/* Fitted x position */
+	     double *yu,		/* Fitted y position */
+	     int *fmax,			/* Highest pixel in best bin */
+	     double *fwhm,		/* FWHM of psf (binx,y corrected) */
+	     double *xfwhm,		/* FWHM in x dir (binx corrected) */
+	     double *yfwhm,		/* FWHM in y dir (biny corrected) */
+	     double *bkgnd,		/* Sky level */
+	     double *ftot,		/* Total flux */
+	     double *weight,		/* Composite quality of star: <1 bad,*/
+	     				/*    1-10 so-so, 10-30 OK, >30 fine */
+	     double *snr,		/* S/N of (big-sky)/noise */
+	     int *binfactor)		/* Best bin factor used */
+{
+   int i, j, k;
+   int xmax, ymax, big, bbig=0;
+   double x0, y0, pfwhm, xpfwhm, ypfwhm, bsize;
+   double flux[LOGMAXBIN+1], sky[LOGMAXBIN+1];
+   double xp[LOGMAXBIN+1], yp[LOGMAXBIN+1], sn[LOGMAXBIN+1];
+   double xfwest[LOGMAXBIN+1], yfwest[LOGMAXBIN+1], fwest[LOGMAXBIN+1];
+   double wgt[LOGMAXBIN+1], bestwgt;
+   int bin, bestbin=0, err[LOGMAXBIN+1];
+   unsigned short int *imptr[LOGMAXBIN+1];
+   int sx[LOGMAXBIN+1], sy[LOGMAXBIN+1];
+   int nx[LOGMAXBIN+1], ny[LOGMAXBIN+1], NX[LOGMAXBIN+1];
+#ifdef DONUT_TEST
+   int fd;
+   char fname[2880];
+#endif
+
+#ifdef DONUT_TEST
+   fprintf(stderr, "nx=%d ny=%d sx=%d sy=%d binx=%d biny=%d data=%d\n", 
+	  ZFnx, ZFny, ZFsx, ZFsy, ZFbinx, ZFbiny, ZFdata[0]);
+#endif
+
+/* Allocate some space for the binned down images */
+   if(binsize < (ZFnx*ZFny+2)/3) {
+      if(binim == NULL) free(binim);
+      binim = (unsigned short int *)calloc((ZFnx*ZFny+2)/3, sizeof(short int));
+      binsize = (ZFnx*ZFny+2) / 3;
+   }
+
+/* Bin down the image and update the arrays */
+   imptr[0] = (unsigned short int *)((ushort *)ZFdata+bordx+bordy*ZFNX);
+   sx[0] = ZFsx + bordx;
+   sy[0] = ZFsy + bordy;
+   nx[0] = ZFnx - 2*bordx;
+   ny[0] = ZFny - 2*bordy;
+   NX[0] = ZFNX;
+   for(bin=1; bin<=LOGMAXBIN; bin++) {
+      if(bin == 1) imptr[bin] = binim;
+      else	   imptr[bin] = imptr[bin-1] + NX[bin-1]*ny[bin-1];
+      sx[bin] = sx[bin-1];
+      sy[bin] = sy[bin-1];
+      nx[bin] = nx[bin-1]/2;
+      ny[bin] = ny[bin-1]/2;
+      NX[bin] = nx[bin];
+      for(j=0; j<ny[bin]; j++) {
+	 for(i=0; i<nx[bin]; i++) {
+	    k  = imptr[bin-1][2*i+2*j*NX[bin-1]];
+	    k += imptr[bin-1][2*i+1+2*j*NX[bin-1]];
+	    k += imptr[bin-1][2*i+(2*j+1)*NX[bin-1]];
+	    k += imptr[bin-1][2*i+1+(2*j+1)*NX[bin-1]];
+	    imptr[bin][i+j*NX[bin]] = (k+2)/4;
+	 }
+      }
+   }
+
+/* Analyze each one */
+   bestwgt = 0.0;
+   for(bin=0; bin<=LOGMAXBIN; bin++) {
+      bsize = pow(2.0, (double)bin);
+
+#ifdef DONUT_TEST
+      sprintf(fname, "/tmp/bin%d.fits", bin);
+      fd = creat(fname, 0664);
+      sprintf(fname, "SIMPLE  =                    T                                                  BITPIX  =                   16                                                  NAXIS   =                    2                                                  NAXIS1  =                 %4d                                                  NAXIS2  =                 %4d                                                  END                                                                             ", nx[bin], ny[bin]);
+      for(i=strlen(fname); i<2880; i++) fname[i] = ' ';
+      write(fd, fname, 2880);
+      swab(imptr[bin], imptr[bin], 2*NX[bin]*ny[bin]);
+      for(j=0; j<ny[bin]; j++) write(fd, imptr[bin]+j*NX[bin], 2*nx[bin]);
+      swab(imptr[bin], imptr[bin], 2*NX[bin]*ny[bin]);
+      close(fd);
+#endif
+      err[bin] = psfmargin_guts(1, &xmax,&ymax, &big, &x0,&y0,    
+	  &pfwhm, &xpfwhm, &ypfwhm, &sky[bin], &flux[bin], &sn[bin], 
+	  nx[bin], ny[bin], NX[bin], imptr[bin]);
+      xp[bin] = x0 * bsize * ZFbinx + sx[bin];
+      yp[bin] = y0 * bsize * ZFbiny + sy[bin];
+      xfwest[bin] = sqrt(MAX(0.01,xpfwhm*xpfwhm - 0.46)) * ZFbinx * bsize;
+      yfwest[bin] = sqrt(MAX(0.01,ypfwhm*ypfwhm - 0.46)) * ZFbiny * bsize;
+      fwest[bin] = sqrt(xfwest[bin]*yfwest[bin]);
+      flux[bin] *= bsize*bsize;
+/* Calculate weights for final guesses */
+/* Error is bad, virtually fatal */
+      wgt[bin] = err[bin] == 0 ? 1.0 : 0.01;
+/* Non-round PSF is very bad */
+      wgt[bin] *= pow(xfwest[bin]/yfwest[bin], 
+		     xfwest[bin] < yfwest[bin] ? +2.0 : -2.0);
+/* Ridiculously small or large PSF is bad, ~2.4 is best */
+      wgt[bin] *= exp(-0.5*pow((log(MAX(0.1,xpfwhm))-log(2.4))/log(2.0), 2.0));
+      wgt[bin] *= exp(-0.5*pow((log(MAX(0.1,ypfwhm))-log(2.4))/log(2.0), 2.0));
+/* High S/N is good */
+      wgt[bin] *= MAX(0.0, sn[bin]);
+/* High flux is good (crudely normalize assuming ~1e/ADU) */
+      wgt[bin] *= MAX(0.0, flux[bin]/1e5);
+
+      if(wgt[bin] > bestwgt) {
+	 bestwgt = wgt[bin];
+	 bestbin = bin;
+	 bbig = big;
+      }
+
+#ifdef DONUT_TEST
+      printf("B%d %2d: %5.1f %5.1f %6.1f %5.1f %4.1f %4.1f %4.1f %4.1f %5d %5.0f %7.0f %6.1f %6.1f\n", 
+	     bin, err[bin], x0, y0, xp[bin], yp[bin], pfwhm, xpfwhm, ypfwhm, fwest[bin],
+	     big, sky[bin], flux[bin], sn[bin], wgt[bin]);
+      drawcirc(xp[bin]/ZFbinx, yp[bin]/ZFbiny, 0.5*fwest[bin], sn[bin]>5?0:1);
+#endif
+   }
+
+#ifdef DONUT_TEST
+   printf("\n");
+   drawcirc((xp[bestbin]-sx[bestbin])/ZFbinx+sx[bestbin], 
+	    (yp[bestbin]-sy[bestbin])/ZFbiny+sy[bestbin], 
+	    0.5*fwest[bestbin]-1, sn[bestbin]>5?0:1);
+#endif
+
+/* Return values */
+   *xu = xp[bestbin];
+   *yu = yp[bestbin];
+   *fmax = bbig;
+   *fwhm = fwest[bestbin];
+   *xfwhm = xfwest[bestbin];
+   *yfwhm = yfwest[bestbin];
+   *bkgnd = sky[bestbin];
+   *ftot = flux[bestbin];
+   *weight = wgt[bestbin];
+   *snr = sn[bestbin];
+   *binfactor = bestbin;
+
+   return(0);
+}
Index: /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/analysis/libpsf/psfmargin.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/analysis/libpsf/psfmargin.c	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/analysis/libpsf/psfmargin.c	(revision 23594)
@@ -0,0 +1,1037 @@
+/* Basic subroutines for computing psf profiles */
+/*   algo:   1's digit: 0 for parabola fit, 1 for Gaussian fit to posn
+ *          10's digit: 0 for quick, dumb flux sum, 1-3 for wing fit and
+ *                      full flux sum, 1,2,3 = wauss, power law, exponential
+ *         100's digit: 0 for normal operation, 1 to ignore right hand double
+ *			(for calcite operations)
+ *     1000000's digit: 1 for debug output
+ */
+/* 080703 JT - slim down border to BORDER instead of n/8 */
+/* 031026 JT - mess around with better flux estimates ((algo/10)%10 = 1) */
+/* 020420 JT - I'm generated NaNs somehow so test for them and remove them! */
+/* 020322 JT - avoid edge pixels like the plague */
+/* 020128 JT - change args to permit non-filled arrays */
+/* 6/20/96 (JT) to return S/N and only one fwhm */
+/* JT 6/25/93 */
+/* Routine to find the biggest star in an image and return its
+ * pixel center (ix,iy), fit center (x0,y0), width (fwhm) and S/N (sn)
+ * The background level is returned as sky
+ * Assumes unsigned short integers
+ **************************CONVENTION******************************
+ * The first element of the array is taken to be pixel 0.0 to 1.0
+ ******************************************************************
+ */
+
+#include <stdio.h>
+#include <math.h>
+#include "psf.h"
+// #define DEBUG	/* Engage debug code? */
+
+#define MAXLINE 4096	/* Maximum storage for various purposes */
+#define MAXDIM	1024	/* Maximum nx or ny */
+#define EDGE  1		/* Number of edge pixels to ignore */
+static int BORDER=2;	/* Closest a high pixel can be to edge */
+
+#define DBLFRAC  0.6	/* Fraction of highest pixel to be a calcite double */
+
+#define NGAUSS  5       /* Number of parameters for a Gaussian fit */
+#define NWING   3       /* Number of parameters for a psf tail fit */
+
+#define NODATA  0	/* Special value for *NO DATA* */
+
+#define XERROR   (0x0001)
+#define YERROR   (0x0002)
+#define GXERROR  (0x0004)
+#define GYERROR  (0x0008)
+#define FWERROR  (0x0010)
+#define WINGERROR  (0x0020)
+#define DATAERROR  (0x0040)
+
+static double strip[MAXLINE];
+static double median(int n, double *key, int *idx);
+int psfmargin_guts(int algo, int *ix, int *iy, int *big, double *x0, double *y0, 
+	       double *fwhm, double *xfwhm, double *yfwhm, 
+	       double *sky, double *flux, double *sn, 
+	       int nx, int ny, int mx, unsigned short *im);
+static int parabola(int nx, double *d, 
+		    double *center, double *height, double *width);
+static int gaussian(int *niter, int n, double *d, double *par);
+static int dgauss(int n, double *d, 
+		  double *par, double *chi, double *deriv, double *curv);
+static int gchi(int n, double *d, double *par, double *chi);
+static int wingfit(int *niter, int func, int n, 
+		   double *d, double *r, double *par);
+static int wingchi(int func, int n, double *r, double *d, 
+	double dmin, double *par, double *chi);
+static int dwingfit(int func, int n, double *r, double *d, double dmin, 
+		    double *par, double *chi, double *deriv, double *curv);
+static int linsolve(int n, double *y, double *a, double *x);
+#ifdef FANCY_STUFF
+static int exponential_bias(double *bias, double *ampl, double *efold, 
+			    int nx,int ny,int mx, unsigned short *im);
+static int video_bias_sub(double bias, double ampl, double efold, 
+			  int nx,int ny,int mx, unsigned short *im);
+#endif
+
+/* Normal, Gaussian fit version */
+int psfmargin(int sx, 			/* x offset of pixel 0,0 */
+	     int sy,  			/* y offset of pixel 0,0 */
+	     int binx,  		/* x bin factor */
+	     int biny,   		/* y bin factor */
+	     int bordx,  		/* x border */
+	     int bordy,   		/* y border */
+	     int nx,   			/* x size of image */
+	     int ny,    		/* y size of image */
+	     int NX,   			/* x stride of image */
+	     unsigned short *data,	/* ushort image data: 0 = *NO_DATA* */
+	     double *xu,		/* Fitted x position */
+	     double *yu,		/* Fitted y position */
+	     int *fmax,			/* Highest pixel in best bin */
+	     double *fwhm,		/* FWHM of psf (binx,y corrected) */
+	     double *xfwhm,		/* FWHM in x dir (binx corrected) */
+	     double *yfwhm,		/* FWHM in y dir (biny corrected) */
+	     double *bkgnd,		/* Sky level */
+	     double *ftot,		/* Total flux */
+	     double *weight,		/* Composite quality of star: <1 bad,*/
+	     				/*    1-10 so-so, 10-30 OK, >30 fine */
+	     double *snr)		/* S/N of (big-sky)/noise */
+{
+   int ix, iy, big, err;
+   BORDER = MAX(bordx, bordy);
+   err = psfmargin_guts(1, &ix,&iy, &big, 
+       xu,yu, fwhm, xfwhm, yfwhm, bkgnd, ftot, snr, nx,ny,NX, data);
+
+   *xu = sx + *xu * binx;
+   *yu = sy + *yu * biny;
+   *fwhm *= sqrt((double)(binx*biny));
+   *xfwhm *= binx;
+   *yfwhm *= biny;
+   *fmax = big;
+   *weight = *snr;
+   return(err);
+}
+
+/* Basic PSF calculation routine */
+int
+psfmargin_guts(algo, ix,iy, big, x0,y0, fwhm, xfwhm, yfwhm, 
+	       sky, flux, sn, nx,ny,mx, im)
+   int algo;			/* What algorithm to use? */
+   int *ix, *iy, *big;		/* Highest pixel */
+   double *x0, *y0;		/* Fitted location of centroid */
+   double *fwhm, *xfwhm, *yfwhm;/* Star fwhm */
+   double *sn;			/* Star signal to noise */
+   double *sky, *flux;		/* Sky level, total flux in star */
+   int nx, ny, mx;		/* Size of image; x size of image storage */
+   unsigned short *im;		/* Data array */
+{
+   int i, j, nsky, iwx, iwy, dblbig=0.0, dblx=0, dbly=0, err=0;
+   int n, k[4];
+   double mean[4], rms[4], var, medsky;
+   double xhgt, yhgt;
+   double gxpar[NGAUSS], gypar[NGAUSS], wingpar[NWING];
+   int niter;
+
+/* Locate the highest pixel in the central 3/4 of the image */
+/* No, avoid pixels closer to the edge than BORDER */
+   *big = -1;
+//   for(j=ny/8; j<(7*ny)/8; j++) {
+//      for(i=nx/8; i<(7*nx)/8; i++) {
+   for(j=BORDER; j<ny-BORDER; j++) {
+      for(i=BORDER; i<nx-BORDER; i++) {
+	 if(im[i+mx*j] != NODATA && (int)im[i+mx*j] > (*big)) {
+	    (*big) = im[i+mx*j];
+	    *ix = i;
+	    *iy = j;
+	 }
+      }
+   }
+/* No pixels found???  (data all *NODATA*) */
+   if(*big == -1) return(DATAERROR);
+
+/* Look in the corners for a sky value */
+   for(i=0; i<4; i++) {
+      mean[i] = rms[i] = 0.0;
+      k[i] = 0;
+   }
+   i = MIN(nx, ny);		/* i = smaller dimension */
+   j = MIN(6, i/8);		/* nsky = dim/8, but 3 <= nsky <= 6 */
+   nsky = MAX(3, j);		/* nsky = dim/8, but 3 <= nsky <= 6 */
+   if(nsky > i/2) nsky = i/3;
+   if(nsky < 1) nsky = 1;
+   for(j=EDGE; j<nsky+EDGE; j++) {
+      for(i=EDGE; i<nsky+EDGE; i++) {
+	 if(im[i+j*mx] != NODATA) {
+	    mean[0] += im[i+j*mx];
+	    rms[0]  += im[i+j*mx] * im[i+j*mx];
+	    k[0] += 1;
+	 }
+	 if(im[nx-1-i+j*mx] != NODATA) {
+	    mean[1] += im[nx-1-i+j*mx];
+	    rms[1]  += im[nx-1-i+j*mx] * im[nx-1-i+j*mx];
+	    k[1] += 1;
+	 }
+	 if(im[nx-1-i+(ny-1-j)*mx] != NODATA) {
+	    mean[2] += im[nx-1-i+(ny-1-j)*mx];
+	    rms[2]  += im[nx-1-i+(ny-1-j)*mx] * im[nx-1-i+(ny-1-j)*mx];
+	    k[2] += 1;
+	 }
+	 if(im[i+(ny-1-j)*mx] != NODATA) {
+	    mean[3] += im[i+(ny-1-j)*mx];
+	    rms[3]  += im[i+(ny-1-j)*mx] * im[i+(ny-1-j)*mx];
+	    k[3] += 1;
+	 }
+      }
+   }
+/* Normalize mean and rms, initialize index array */
+   for(i=0; i<4; i++) {
+      if(k[i] > 0) mean[i] /= k[i];
+      if(k[i] > 0) rms[i] /= k[i];
+      k[i] = i;
+   }
+/* Sort them via index array k[] */
+   if(mean[k[2]] > mean[k[3]]) {i=k[2]; k[2]=k[3]; k[3]=i;}
+   if(mean[k[1]] > mean[k[2]]) {i=k[1]; k[1]=k[2]; k[2]=i;}
+   if(mean[k[2]] > mean[k[3]]) {i=k[2]; k[2]=k[3]; k[3]=i;}
+   if(mean[k[0]] > mean[k[1]]) {i=k[0]; k[0]=k[1]; k[1]=i;}
+   if(mean[k[1]] > mean[k[2]]) {i=k[1]; k[1]=k[2]; k[2]=i;}
+   if(mean[k[2]] > mean[k[3]]) {i=k[2]; k[2]=k[3]; k[3]=i;}
+
+/* Pick the second greatest as the sky level */
+   i = 1;
+   if(mean[k[i]] == NODATA) i = 2;
+   if(mean[k[i]] == NODATA) i = 3;
+   *sky = mean[k[i]];
+   if(nsky > 1) var = rms[k[i]] - (*sky)*(*sky);
+   else var = (mean[k[MIN(i+1,3)]]-mean[k[i]])*(mean[k[MIN(i+1,3)]]-mean[k[i]]);
+   if(var < 1.0) var = 1.0;
+
+/* Check to see whether there is another, nearly equivalent star to the left */
+   if((algo/100)%10 == 1) {
+//      for(j=ny/8; j<(7*ny)/8; j++) {
+//	 for(i=nx/8; i<(7*nx)/8; i++) {
+      for(j=BORDER; j<ny-BORDER; j++) {
+	 for(i=BORDER; i<nx-BORDER; i++) {
+/* Skip first star we found */
+	    if(ABS(i-*ix) < 2 && ABS(j-*iy) < 2) continue;
+/* Is it nearly as tall? */
+	    if(im[i+mx*j] != NODATA &&
+	       (int)im[i+mx*j] > ((*big)-(*sky))*DBLFRAC+(*sky)) {
+/* Must be a local max */
+	       if(im[i+mx*j]>im[i-1+mx*j] && im[i+mx*j]>=im[i+1+mx*j] &&
+		  im[i+mx*j]>im[i+mx*(j-1)] && im[i+mx*j]>=im[i+mx*(j+1)]) {
+		  dblbig = im[i+mx*j];
+		  dblx = i;
+		  dbly = j;
+	       }
+	    }
+	 }
+      }
+/* Does a double star exist and is it to the left? */
+      if(dblbig > 0 && dblx < *ix) {
+	 *big = dblbig;
+	 *ix = dblx;
+	 *iy = dbly;
+      }
+   }
+
+/*  printf("%d %d %d %d %.1f %.1f\n",nsky, i,i0,i1,*sky,sqrt(var)); */
+
+/* "Signal to noise" is the ratio of the brightest pixel to the rms */
+   *sn = ((*big) - *sky) / sqrt(var);
+
+/* Estimate a width for the peak in x */
+   for(i=(*ix)+1; i<nx; i++) 
+      if(im[i+(*iy)*mx] != NODATA && im[i+(*iy)*mx] < ((*big)+(*sky))/2) break;
+   iwx = i - (*ix);
+
+/* Estimate a width for the peak in y */
+   for(i=(*iy)+1; i<ny; i++)
+      if(im[(*ix)+i*mx] != NODATA && im[(*ix)+i*mx] < ((*big)+(*sky))/2) break;
+   iwy = i - (*iy);
+
+/* Extract a strip in x */
+   for(i=EDGE; i<nx-EDGE; i++) {
+      strip[i] = 0.0;
+      n = 0;
+      for(j=MAX(EDGE,(*iy)-iwy); j<= MIN(ny-1-EDGE,(*iy)+iwy); j++) {
+	 if(im[i+j*mx] != NODATA) {
+	    strip[i] += im[i+j*mx];
+	    n++;
+	 }
+      }
+      if(n > 0) strip[i] =  strip[i] / n - (*sky);
+   }
+
+/* Find the peak in x */
+   if((algo%10) == 0) {
+      if(parabola(nx, strip, x0, &xhgt, xfwhm)) err |= XERROR ;
+   } else if((algo%10) == 1) {
+      niter = 20;
+      if(gaussian(&niter, nx-2*EDGE, strip+EDGE, gxpar) == 0) {
+	 *x0 = gxpar[3] + EDGE;
+	 *xfwhm = 2.4*gxpar[4];
+      } else {
+	 err |= GXERROR;
+	 if(parabola(nx, strip, x0, &xhgt, xfwhm)) err |= XERROR;
+      }
+   }
+
+   if((algo/1000000)%10 == 1) {
+      printf("%3d %3d %6.1f %8.3f %8.2f %8.2f %8.2f\n", 
+	     *iy, iwy, gxpar[0], gxpar[1],gxpar[2], gxpar[3],gxpar[4]);
+      for(i=0; i<nx-2*EDGE; i++) printf("%4d %8.1f\n", i, strip[EDGE+i]);
+   }
+
+/* Extract a strip in y */
+   for(j=EDGE; j<ny-EDGE; j++) {
+      strip[j] = 0.0;
+      n = 0;
+      for(i=MAX(EDGE,(*ix)-iwx); i<=MIN(nx-1-EDGE,(*ix)+iwx); i++) {
+	 if(im[i+j*mx] != NODATA) {
+	    strip[j] += im[i+j*mx];
+	    n++;
+	 }
+      }
+      if(n > 0) strip[j] =  strip[j] / n - (*sky);
+   }
+
+/* Find the peak in y */
+   if((algo%10) == 0) {
+      if(parabola(ny, strip, y0, &yhgt, yfwhm)) err |= YERROR;
+   } else if((algo%10) == 1) {
+      niter = 20;
+      if(gaussian(&niter, ny-2*EDGE, strip+EDGE, gypar) == 0) {
+	 *y0 = gypar[3] + EDGE;
+	 *yfwhm = 2.4*gypar[4];
+      } else {
+	 err |= GYERROR;
+	 if(parabola(ny, strip, y0, &yhgt, yfwhm)) err |= YERROR;
+      }
+   }
+
+   if((algo/1000000)%10 == 1) {
+      printf("%3d %3d %6.1f %8.3f %8.2f %8.2f %8.2f\n", 
+	     *ix, iwx, gypar[0], gypar[1],gypar[2], gypar[3],gypar[4]);
+      for(i=0; i<ny-2*EDGE; i++) printf("%4d %8.1f\n", i, strip[EDGE+i]);
+   }
+
+/* net FWHM = sqrt(fwx * fwy) */
+   *fwhm = (*xfwhm) * (*yfwhm);
+
+/* Sanity check on x0, y0 and FWHM, please! */
+   if(*fwhm <= 0 || 
+      *x0 < EDGE || *x0 > nx-1-EDGE || *y0 < EDGE || *y0 > ny-1-EDGE) {
+      *x0 = *ix;
+      *y0 = *iy;
+      *fwhm = *xfwhm = *yfwhm = MIN(nx,ny) / 4;
+      err |= FWERROR;
+   } else {
+      *fwhm = sqrt(*fwhm);
+   }
+
+/* Add up the total flux in excess of sky */
+/* Quick, dumb sum of +/-2.5 FWHM less sky */
+   if(((algo/10)%10) == 0) {
+      for(j=-2.5*(*fwhm), *flux=0.0; j<=2.5*(*fwhm); j++) {
+	 if(j+(*iy) >= EDGE && j+(*iy) <= ny-1-EDGE) {
+	    for(i=-2.5*(*fwhm); i<=2.5*(*fwhm); i++) {
+	       if(i+(*ix) >= EDGE && i+(*ix) <= nx-1-EDGE &&
+		   im[i+(*ix)+mx*(j+(*iy))] != NODATA) {
+		  *flux +=  im[i+(*ix)+mx*(j+(*iy))] - (*sky);
+	       }
+	    }
+	 }
+      }
+
+   } else {
+      int r, rmin, rmax, counts[2*MAXDIM], idx[2*MAXDIM], func;
+
+      func = ((algo/10)%10) - 1;
+
+/* This gets medsky from a square ring of outermost pixels */
+      for(j=EDGE, n=0; j<ny-EDGE; j++) {
+	 if(j >= EDGE+3 && j <= ny-1-EDGE-3) continue;
+	 for(i=EDGE; i<nx-EDGE; i++) {
+	    if(i >= EDGE+3 && i <= nx-1-EDGE-3) continue;
+	    if(im[i+mx*j] != NODATA) {
+	       strip[n++] =  im[i+mx*j];
+	    }
+	 }
+      }
+      medsky = median(n, strip, NULL);
+
+/* Sum up medians as a function of radius */
+      rmax = 0.65 * MIN(nx, ny) - EDGE;
+      rmin = MAX(2, 1.5*(*fwhm));
+      rmin = MIN(rmin, rmax-4);
+      for(r=rmin; r<=rmax; r++) {
+	 counts[r] = 0;
+	 idx[r] = 3.2 * r*r;
+      }
+/* Accumulate the pixels in the skirt of the psf into arrays by radius */
+      for(j=EDGE; j<ny-EDGE; j++) {
+	 for(i=EDGE; i<nx-EDGE; i++) {
+	    r = sqrt((i+0.5-(*x0))*(i+0.5-(*x0))+(j+0.5-(*y0))*(j+0.5-(*y0))) + 0.5;
+	    if(r<rmin || r>rmax) continue;
+	    strip[idx[r]+counts[r]] =  im[i+mx*j];
+	    counts[r] += 1;
+	 }
+      }
+/* Find the median as a function of radius*/
+      for(r=rmin; r<=rmax; r++) {
+	 strip[r] = r;
+	 strip[r+idx[rmin]] = median(counts[r], &strip[idx[r]], NULL);
+	 if((algo/1000000)%10 == 1) printf("%4d %4d %4d %6.1f\n", 
+				       r, counts[r], idx[r], strip[r+idx[rmin]]);
+      }
+
+      niter = 20;
+      if(wingfit(&niter, func, rmax-rmin+1, strip+idx[rmin]+rmin, strip+rmin, 
+		 wingpar)) err |= WINGERROR;
+#ifdef DEBUG
+      printf("wing %3d %7.2f %7.2f %7.2f\n", 
+	     niter, wingpar[0], wingpar[1], wingpar[2]);
+#endif
+/* power law fit sky */
+      *sky = wingpar[0];
+/* outermost circular ring */
+//      *sky = strip[rmax+idx[rmin]];
+/* outermost square pixels */
+//      *sky = medsky;
+/* Fixed sky at bias plus a bit */
+//	      *sky = 202;
+
+      for(j=EDGE, *flux=0.0; j<ny-EDGE; j++) {
+	 for(i=EDGE; i<nx-EDGE; i++) {
+	    *flux +=  im[i+mx*j] - *sky;
+	 }
+      }
+/* Add in a bit more from extrapolation of the extended wings */
+/*
+      rpow = 0.65*nx;
+      wing = -wingpar[1]*(2*3.14159) /(2+wingpar[2]) * pow(rpow,wingpar[2]+2);
+      *big = wing / (*flux) * 10000;
+      *flux +=  wing;
+      *flux -= 0.61 * wing;
+*/
+   }
+
+/*
+  printf("(%d,%d) (%.1f,%.1f) (%.1f,%.1f) %.1f %.1f %.1f %.1f %.1f %.0f %d\n",
+  *ix,*iy,*x0,*y0,x1,y1,*fwhm,r2,*sky,*sn,f0,*flux,MAX(iwx,iwy));
+  */
+   return(err);
+}
+
+/* Subroutine to fit a parabola to some data (fast)
+ * N         = n, where 2n+1 points are fitted
+ * CENTER    = center of fit parabola
+ * HEIGHT    = height of fit parabola
+ * WIDTH     = width of fit parabola
+ **************************CONVENTION******************************
+ The first element of the array is taken to be pixel 0.0 to 1.0
+ ******************************************************************
+ */
+
+static int
+parabola(nx, d, center, height, width)
+   int nx;
+   double *center, *height, *width, *d;
+{
+   double sum0=0.0, sum1=0.0, sum2=0.0, big;
+   float a, b, c;
+   int i, j, k, n;
+
+/* Find the center and estimate a width for the peak */
+   for(i=EDGE, big=d[EDGE], k=0; i<nx-EDGE; i++) {
+      if(d[i] > big) {
+	 big = d[i];
+	 k = i;
+      }
+   }
+  
+   for(i=k+1;  i<nx-EDGE; i++) if(d[i] < big/2) break;
+   for(j=k-1; j>=EDGE; j--) if(d[j] < big/2) break;
+   n = (i-j) / 2;
+   if(k-n < EDGE || k+n > nx-1-EDGE) n = MIN(k-EDGE, nx-1-EDGE-k);
+
+   if(n < 1) {
+      *center = k + 0.5;
+      *width = 0.0;
+      *height = big;
+      return(-1);
+   }
+
+   for(i=(-n); i<=n; i++) {
+      sum0 = sum0 + d[k+i];
+      sum1 = sum1 + i*d[k+i];
+      sum2 = sum2 + i*i*d[k+i];
+   }
+
+   a = 45.*(sum2 - n*(n+1)/3.*sum0) / (n*(n+1)*(2*n-1)*(2*n+1)*(2*n+3));
+   b = -3.*sum1 / (n*(n+1)*(2*n+1));
+   c = sum0 / (2*n+1);
+
+   if(a >= 0.0) {
+      *center = k + 0.5;
+      *width = 0.0;
+      *height = big;
+      return(-2);
+   }
+   *center = k + 0.5 + b/(2*a);
+   *height = c - a * (n*(n+1)/3. + b*b/(4*a*a));
+   *width = -2 * (*height) / a;
+
+/*
+  printf("\n");
+  for(i=k-4; i<=k+4; i++)
+  printf(" %8d", (int)d[i]);
+  printf("\n%4d %10.3f %10.3f %10.3f %10.3f %10.3f %10.3f\n",
+  k,a,b,c,*center,*width,*height);
+*/
+   if( *width > 0) *width = sqrt(*width);
+   else *width = 0.0;
+
+/* NaN sanity checks */
+   if( !(*center < 0.0 || *center >= 0.0) ||
+       !(*width  < 0.0 || *width  >= 0.0) ||
+       !(*height < 0.0 || *height >= 0.0) ) {
+      fprintf(stderr, "\nCorrecting a ctr NaN, nx = %d, k = %d, n = %d, max = %f\n", 
+	      nx, k, n, big);
+      *center = k + 0.5;
+      *width = 0.0;
+      *height = big;
+   }
+
+   return(0);
+}
+
+#define SKY 0
+#define SLP 1
+#define HGT 2
+#define CTR 3
+#define SIG 4
+
+#define TOL 0.1
+
+static int
+gaussian(int *niter, int n, double *d, double *par)
+/* par[0-4] = sky, slope, height, center, width */
+{
+   double *dfit, deriv[NGAUSS], curv[NGAUSS*NGAUSS];
+   double b[NGAUSS], delta[NGAUSS], a[NGAUSS*NGAUSS];
+   double lambda, chi, chinew, sqrt10;
+   int i, j, max, nfit, iter;
+
+#ifdef DEBUG
+   printf("%d", n);
+   for(i=0; i<n; i++) printf(" %4.0f", d[i]);
+   printf("\n");
+#endif
+
+/* Get some parameter estimates */
+   par[SKY] = 0.5*(d[0] + d[1]);
+   par[SLP] = (0.5*(d[n-1] + d[n-2]) - par[SKY]) / (n-2);
+   for(i=1, max=0, par[HGT]=d[0]; i<n; i++) {
+      if(d[i] > par[HGT]) {
+	 max = i;
+	 par[HGT] = d[i];
+      }
+   }
+   par[HGT] -= par[SKY] + (max+0.5)*par[SLP];
+
+   for(j=max+1; j<n; j++) {
+      if(d[j] < par[SKY] + (j+0.5)*par[SLP] + 0.5*par[HGT]) break;
+   }
+   for(i=max-1; i>=0; i--) {
+      if(d[i] < par[SKY] + (i+0.5)*par[SLP] + 0.5*par[HGT]) break;
+   }
+   par[SIG] = 0.25 * (j-i);
+/* Restrict fit to just the peak; Gaussians suck otherwise */
+   nfit = MAX(9, 2*(j-i)+1);
+   if(max - (nfit-1)/2 < 0 || max + (nfit-1)/2 > n-1)
+      nfit = MIN(2*max+1, 2*(n-1-max)+1);
+   dfit = d + max - (nfit-1)/2;
+   par[SKY] += (max-(nfit-1)/2) * par[SLP];
+   par[CTR] = (nfit-1)/2;
+
+   sqrt10 = sqrt(10.0);
+
+/* Do some Marquardt iterations */
+/*
+   printf("\nnfit = %4d\n", nfit);
+*/
+   for(iter=0, lambda=0.1; iter<*niter; iter++) {
+      dgauss(nfit, dfit, par, &chi, deriv, curv);
+#ifdef DEBUG
+      printf("%4d %7.4f %7.1f %7.1f %7.1f %7.2f %7.3f %7.1f\n", iter, lambda, 
+	     par[SKY], par[SLP], par[HGT], par[CTR], par[SIG], chi);
+#endif
+      for(j=0; j<NGAUSS; j++) {
+	 b[j] = deriv[j];
+	 for(i=0; i<NGAUSS; i++) {
+	    a[i+j*NGAUSS] = curv[i+j*NGAUSS];
+	 }
+	 a[j+j*NGAUSS] *= (1+lambda);
+      }
+      if(linsolve(NGAUSS, b, a, delta) != 0) { /* probably in very bad shape */
+/* 2 means singular matrix -- these should be understood and debugged */
+	 return(-1);
+      }
+      for(j=0; j<NGAUSS; j++) par[j] -= delta[j];
+      gchi(nfit, dfit, par, &chinew);
+      if(chi-chinew < TOL) {
+	 par[CTR] += max - (nfit-1)/2;
+	 *niter = iter;
+/*	 printf("n = %d", *niter); */
+	 return(0);
+      } else if(chinew < chi) {
+	 lambda /= sqrt10;
+      } else {
+	 lambda *= sqrt10;
+	 for(j=0; j<NGAUSS; j++) par[j] += delta[j];
+      }
+   }
+   *niter = iter;
+   return(1);
+}
+
+static int
+gchi(int n, double *d, double *par, double *chi)
+/* par[0-4] = sky, slope, height, center, width */
+{
+   double z, expo, diff;
+   int i;
+   for(i=0, *chi = 0.0; i<n; i++) {
+      z = i + 0.5;
+      expo = exp(-0.5*(z-par[CTR])*(z-par[CTR])/(par[SIG]*par[SIG]));
+      diff = par[SKY] + par[SLP]*z + par[HGT]*expo - d[i];
+      *chi += diff * diff;
+   }
+   return(0);
+}
+
+static int
+dgauss(int n, double *d, double *par, double *chi, double *deriv, double *curv)
+/* par[0-4] = sky, slope, height, center, width */
+{
+   double z, expo, diff, df[NGAUSS];
+   int i, j, k;
+   *chi = 0.0;
+   for(i=0; i<NGAUSS; i++) deriv[i] = 0.0;
+   for(i=0; i<NGAUSS*NGAUSS; i++) curv[i] = 0.0;
+   df[SKY] = 1.0;
+   for(i=0; i<n; i++) {
+      z = i + 0.5;
+      expo = exp(-0.5*(z-par[CTR])*(z-par[CTR])/(par[SIG]*par[SIG]));
+      diff = par[SKY] + par[SLP]*z + par[HGT]*expo - d[i];
+      *chi += diff * diff;
+      df[SLP] = z;
+      df[HGT] = expo;
+      df[CTR] = par[HGT] * expo * (z-par[CTR]) / (par[SIG]*par[SIG]);
+      df[SIG] = par[HGT] * expo * (z-par[CTR])*(z-par[CTR]) / 
+	 (par[SIG]*par[SIG]*par[SIG]);
+      for(j=0; j<NGAUSS; j++) {
+	 deriv[j] += diff * df[j];
+	 for(k=j; k<NGAUSS; k++) {
+	    curv[j+k*NGAUSS] += df[j] * df[k];
+	 }
+      }
+   }
+   for(j=1; j<NGAUSS; j++) {
+      for(k=0; k<j; k++) {
+	 curv[j+k*NGAUSS] = curv[k+j*NGAUSS];
+      }
+   }
+   return(0);
+}
+
+#define SKY 0
+#define AMP 1
+#define XPO 2
+#define PWRTOL 0.0001
+#define B4 1.0
+#define B6 0.5
+
+static int
+wingfit(int *niter, int func, int n, double *d, double *r, double *par)
+/* func = 0,1,2 for waussian, power law, exponential */
+/* par[0-2] = sky, amplitude, exponent */
+{
+   double deriv[NWING], curv[NWING*NWING];
+   double b[NWING], delta[NWING], a[NWING*NWING];
+   double lambda, chi, chinew, sqrt10, dmin;
+   int i, j, iter;
+
+#ifdef DEBUG
+   printf("%d\n", n);
+   for(i=0; i<n; i++) printf(" %4.0f", r[i]);
+   printf("\n");
+   for(i=0; i<n; i++) printf(" %4.0f", d[i]);
+   printf("\n");
+#endif
+
+/* Get some parameter estimates */
+   par[SKY] = d[n-1];
+   if(func == 0) {		/* Waussian */
+      par[XPO] = 5;
+      par[AMP] = (d[0]-d[n-1]) / exp(-0.5*r[0]*r[0]/(par[XPO]*par[XPO]));
+   } else if(func == 1) {	/* power law */
+      par[XPO] = -3.0;
+      par[AMP] = (d[0]-d[n-1]) / pow(r[0], par[XPO]);
+   } else if(func == 2) {	/* exponential */
+      par[XPO] = -0.1;
+      par[AMP] = (d[0]-d[n-1]) / exp(r[0]*par[XPO]);
+   }
+
+   for(i=1, dmin=d[0]; i<n; i++) dmin = MIN(dmin, d[i]);
+   dmin = 0.5 * dmin;
+
+   sqrt10 = sqrt(10.0);
+
+/* Do some Marquardt iterations */
+   for(iter=0, lambda=0.1; iter<*niter; iter++) {
+      dwingfit(func, n, r, d, dmin, par, &chi, deriv, curv);
+#ifdef DEBUG
+      printf("%4d %7.4f %7.1f %7.1f %7.3f %7.1f\n", iter, lambda, 
+	     par[SKY], par[AMP], par[XPO], chi);
+#endif
+      for(j=0; j<NWING; j++) {
+	 b[j] = deriv[j];
+	 for(i=0; i<NWING; i++) {
+	    a[i+j*NWING] = curv[i+j*NWING];
+	 }
+	 a[j+j*NWING] *= (1+lambda);
+      }
+      if(linsolve(NWING, b, a, delta) != 0) return(-1);
+      for(j=0; j<NWING; j++) par[j] -= delta[j];
+      wingchi(func, n, r, d, dmin, par, &chinew);
+
+      if(chi < chinew) {
+	 lambda *= sqrt10;
+	 for(j=0; j<NWING; j++) par[j] += delta[j];
+      } else if(chi-chinew < PWRTOL) {
+	 *niter = iter;
+	 return(0);
+      } else if(chinew < chi) {
+	 lambda /= sqrt10;
+      }
+   }
+   *niter = iter;
+   return(1);
+}
+
+static int
+wingchi(int func, int n, double *r, double *d, 
+	double dmin, double *par, double *chi)
+/* par[0-2] = sky, amplitude, exponent */
+{
+   double diff, z2, expo=0.0;
+   int i;
+   for(i=0, *chi = 0.0; i<n; i++) {
+      if(func == 0) {		/* Waussian */
+	 z2 = 0.5 * r[i]*r[i] / (par[XPO]*par[XPO]);
+	 expo = 1/(1+z2*(1+z2*(B4/2+z2*B6/6)));
+      } else if(func == 1) {	/* power law */
+	 expo = pow(r[i], par[XPO]);
+      } else if(func == 2) {	/* exponential */
+	 expo = exp(r[i]*par[XPO]);
+      }
+      diff = par[SKY] + par[AMP]*expo - d[i];
+      *chi += diff * diff / ((d[i]-dmin)*(d[i]-dmin));
+   }
+   return(0);
+}
+
+static int
+dwingfit(int func, int n, double *r, double *d, double dmin, double *par, 
+	  double *chi, double *deriv, double *curv)
+/* par[0-2] = sky, amplitude, exponent */
+{
+   double expo=0.0, diff, df[NWING], z2;
+   int i, j, k;
+   *chi = 0.0;
+   for(i=0; i<NWING; i++) deriv[i] = 0.0;
+   for(i=0; i<NWING*NWING; i++) curv[i] = 0.0;
+   df[SKY] = 1.0;
+   for(i=0; i<n; i++) {
+      if(func == 0) {	/* Waussian */
+	 z2 = 0.5 * r[i]*r[i] / (par[XPO]*par[XPO]);
+	 expo = 1 / (1+z2*(1+z2*(B4/2+z2*B6/6)));
+	 df[XPO] = par[AMP] * expo*expo * (1+z2*(B4+z2*B6/2)) * 
+	    r[i]*r[i] / (par[XPO]*par[XPO]*par[XPO]);
+      } else if(func == 1) {	/* power law */
+	 expo = pow(r[i], par[XPO]);
+	 df[XPO] = par[AMP] * log(r[i]) * expo;
+      } else if(func == 2) {	/* exponential */
+	 expo = exp(par[XPO]*r[i]);
+	 df[XPO] = par[AMP] * r[i] * expo;
+      }
+      diff = par[SKY] + par[AMP]*expo - d[i];
+      *chi += diff * diff / ((d[i]-dmin)*(d[i]-dmin));
+      df[AMP] = expo;
+      for(j=0; j<NWING; j++) {
+	 deriv[j] += diff * df[j] / ((d[i]-dmin)*(d[i]-dmin));
+	 for(k=j; k<NWING; k++) {
+	    curv[j+k*NWING] += df[j] * df[k] / ((d[i]-dmin)*(d[i]-dmin));
+	    if(j==XPO && k==XPO) { /* second deriv, probably not nec */
+	       if(func == 1) {		/* power law */
+		  curv[j+k*NWING] += diff * df[XPO] * log(r[i]) / 
+		     ((d[i]-dmin)*(d[i]-dmin)); 
+	       } else if(func == 2) {	/* exponential */
+		  curv[j+k*NWING] += diff * df[XPO] * r[i] / 
+		     ((d[i]-dmin)*(d[i]-dmin)); 
+	       }
+	    }
+	    if(j==AMP && k==XPO) { /* second deriv, probably not nec */
+	       if(func == 1) {		/* power law */
+		  curv[j+k*NWING] += diff * expo * log(r[i]) / 
+		     ((d[i]-dmin)*(d[i]-dmin)); 
+	       } else if(func == 2) {	/* exponential */
+		  curv[j+k*NWING] += diff * expo * r[i] / 
+		     ((d[i]-dmin)*(d[i]-dmin)); 
+	       }
+	    }
+	 }
+      }
+   }
+   for(j=1; j<NWING; j++) {
+      for(k=0; k<j; k++) {
+	 curv[j+k*NWING] = curv[k+j*NWING];
+      }
+   }
+   return(0);
+}
+
+#ifdef FANCY_STUFF
+static int
+exponential_bias(bias, ampl, efold, nx,ny,mx, im)
+   double *bias;
+   double *ampl, *efold;	/* Sky level at bottom * exp(efold*y) */
+   int nx, ny, mx;		/* Size of image; x size of image storage */
+   unsigned short *im;		/* Data array */
+{
+   int i, j, k, nsort, n;
+   double a, b, c, yave, eave, yeave, e2ave, chimin=0.0;
+
+/* Assemble medians in x as a function of y, fit exponential + constant */
+   for(j=EDGE, n=0; j<ny-EDGE; j++, n++) {
+      for(i=EDGE, nsort=0; i<=nx-1-EDGE; i++) {
+	 strip[ny+nsort++] = im[i+j*mx];
+      }
+      strip[n] = median(nsort, strip+ny, NULL);
+/*
+      fprintf(stderr,"%2d %6.2f\n",j, strip[n]);
+*/
+   }
+
+/* Evaluate chi^2 as a function of exponential arg */
+   for(k=0; k<100; k++) {
+      b = 0.002 * (k-50);	/* b = -0.1:+0.1 efold test */
+      for(i=0, j=EDGE, yave=eave=yeave=e2ave=0.0; i<n; i++, j++) {
+	 yave += strip[i];
+	 eave += exp(b*j);
+	 yeave += strip[i] * exp(b*j);
+	 e2ave += exp(2*b*j);
+      }
+      a = (yeave - yave*eave/n) / MAX(1e-10,e2ave-eave*eave/n);
+      c = (yave - a*eave)/n;
+/* Stash chi^2 off end of strip array */
+      for(i=0, j=EDGE, strip[k+n]=0.0; i<n; i++, j++) {
+	 strip[k+n] += (strip[i]-c-a*exp(b*j)) * (strip[i]-c-a*exp(b*j));
+      }
+/*
+      fprintf(stderr,"%.3f %6.2f ",b, strip[n+k]);
+*/
+/* Find the best fit as a function of k */
+      if(k == 0 || strip[n+k] <= chimin) {
+	 *ampl = a;
+	 *efold = b;
+	 *bias = c;
+	 chimin = strip[n+k];
+      }
+   }
+/*
+   fprintf(stderr,"\n");
+   fprintf(stderr,"%.3f %.3f %.3f\n", *ampl, *efold, *bias);
+*/
+   return(0);
+}
+
+static int
+video_bias_sub(bias, ampl, efold, nx,ny,mx, im)
+   double bias;
+   double ampl, efold;		/* Sky level at bottom * exp(efold*y) */
+   int nx, ny, mx;		/* Size of image; x size of image storage */
+   unsigned short *im;		/* Data array */
+{
+   int i, j;
+   double t;
+   for(j=EDGE; j<ny-EDGE; j++) {
+      for(i=EDGE; i<nx-EDGE; i++) {
+	 t = im[i+j*mx] - bias - ampl * exp(efold*j);
+	 if(t >= 0)  im[i+j*mx] = (int)(t+0.5);
+	 else	     im[i+j*mx] = 0;
+/*
+	 if(j<EDGE || j>=ny-EDGE || i<EDGE || i>=nx-EDGE) im[i+j*mx] = 0;
+*/
+      }
+   }
+   return(0);
+}
+#endif
+
+#if 0
+static int
+old_video_bias_sub(bias, ampl, efold, nx,ny,mx, im)
+   double bias;
+   double ampl, efold;		/* Sky level at bottom * exp(efold*y) */
+   int nx, ny, mx;		/* Size of image; x size of image storage */
+   unsigned short *im;		/* Data array */
+{
+   int i, j;
+   double median;
+/*
+   for(j=EDGE; j<ny-EDGE; j++) {
+      for(i=EDGE; i<=nx-1-EDGE; i++) {
+*/
+   for(j=0; j<ny; j++) {
+      for(i=0; i<nx; i++) {
+	 median = exp(ampl + efold*j);
+	 im[i+j*mx] -= median + bias;
+      }
+   }
+   return(0);
+}
+
+static int
+old_exponential_bias(bias, ampl, efold, nx,ny,mx, im)
+   double *bias;
+   double *ampl, *efold;	/* Sky level at bottom * exp(efold*y) */
+   int nx, ny, mx;		/* Size of image; x size of image storage */
+   unsigned short *im;		/* Data array */
+{
+   int i, j, nsort, n;
+   double sx, sx2, sy, sy2, sxy, med;
+   double delx, dely, delxy;
+
+/* Assemble medians in x as a function of y, fit linear function */
+   sx = sx2 = sy = sy2 = sxy = 0;
+   for(j=EDGE, n=0; j<ny-EDGE; j++) {
+      for(i=EDGE, nsort=0; i<=nx-1-EDGE; i++) {
+	 strip[nsort++] = log(MAX(1.0, im[i+j*mx]-*bias));
+      }
+      med = median(nsort, strip, NULL);
+      n++;
+      sx += j;
+      sx2 += j*j;
+      sy += med;
+      sy2 += med*med;
+      sxy += j*med;
+/*      fprintf(stderr,"%2d %6.2f",n, med); */
+   }
+/*   fprintf(stderr,"\n"); */
+
+/* Fit the profile now with a linear (i.e. exponential) function */
+   delx = n*sx2 - sx*sx;
+   dely = n*sy2 - sy*sy;
+   delxy = n*sxy - sx*sy;
+   if(delx != 0.0) {
+      *ampl = (sx2*sy - sx*sxy) / delx;
+      *efold = delxy / delx;
+   } else {
+      *ampl = *bias;
+      *efold = 0.0;
+   }
+
+   return(0);
+}
+#endif
+
+/* median.c : simple bubble sort, carry another array, return median
+ *
+ * 020130 John Tonry
+ */
+static
+double median(int n, double *key, int *idx)
+{
+   int i, j, k, itmp=0;
+   double tmp;
+
+   for(j=n-2; j>=0; j--) {
+      for(i=j+1, k=j; i<n; i++) {
+	 if(key[j] <= key[i]) break;
+	 k = i;
+      }
+      if(k == j) continue;
+      tmp = key[j];
+      if(idx != NULL) itmp = idx[j];
+      for(i=j+1; i<=k; i++) {
+	 key[i-1] = key[i];
+	 if(idx != NULL) idx[i-1] = idx[i];
+      }
+      key[k] = tmp;
+      if(idx != NULL) idx[k] = itmp;
+   }
+   tmp = 0.5 * (key[n/2]+key[(n-1)/2]);		/* Median */
+   return(tmp);
+}
+
+/* linsolve.c - solve a set of linear equations */
+/*
+ * Solves the matrix equation   Y = A X,   returns X.
+ *        where y[j=0,n-1], x[i=0,n-1], a[i+j*n].
+ */
+/* 020211 - John Tonry */
+#define MAXEQ 100
+
+static int
+linsolve(int n, double *y, double *a, double *x)
+{
+   int i, j, k;
+   int rowstatus[MAXEQ], row[MAXEQ];
+   double rat;
+
+   if(n > MAXEQ) {
+      fprintf(stderr, "error: too many equations %d req %d max\n", n, MAXEQ);
+      return(1);
+   }
+
+   for(i=k=0; i<n; i++) rowstatus[i] = 0;
+
+/* Solve matrix by Gaussian elimination */
+   for(j=0; j<n; j++) {		/* j = column to be zero'ed out */
+
+/* Find a good equation to work on (pivot row) */
+      for(i=0, rat=0.0; i<n; i++) {
+	 if(rowstatus[i]) continue;
+	 if(ABS(a[j+i*n]) > rat) {
+	    k = i;
+	    rat = ABS(a[j+k*n]);
+	 }
+      }
+      rowstatus[k] = 1;
+      row[j] = k;
+      if(rat == 0.0) {
+/*
+	 fprintf(stderr, "WHOA: singular matrix, col %d, row %d\n", j, k);
+*/
+	 return(2);
+      }
+/* Subtract away matrix below diagonal */
+      for(i=0; i<n; i++) {	/* i = subsequent equations */
+	 if(rowstatus[i]) continue;
+	 rat = a[j+i*n] / a[j+row[j]*n];
+	 y[i] -= rat*y[row[j]];
+ 	 for(k=j+1; k<n; k++) a[k+i*n] -= rat*a[k+row[j]*n];
+      }
+   }
+
+/* Back substitute into upper diagonal matrix to solve for parameters */
+   for(j=n-1; j>=0; j--) {
+      x[j] = y[row[j]];
+      for(k=j+1; k<n; k++) x[j] -= a[k+row[j]*n]*x[k];
+      x[j] = x[j] / a[j+row[j]*n];
+   }
+   return(0);
+}
Index: /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/analysis/libpsf/psfmoment.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/analysis/libpsf/psfmoment.c	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/analysis/libpsf/psfmoment.c	(revision 23594)
@@ -0,0 +1,233 @@
+/* psfmoment.c: find and fit the brightest star in an image; donuts OK */
+
+/* Note: this is not as fast as psf() nor as accurate as jtpsf() for
+ * flux, width, and position, but it is tolerant of donuts and should
+ * provide a pretty good width and center.
+ */
+/* 080815 v1.0 John Tonry */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/file.h>
+#include <string.h>
+#include <math.h>
+#include "psf.h"
+
+//#define TEST
+
+// #define XBORD 4		/* Disregard this border on the x sides */
+// #define YBORD 2		/* Disregard this border on the y sides */
+#define SIGCLIP 5.0	/* Sky clip level */
+#define FWRMS 2.3548	/* Conversion factor: FWHM/RMS */
+#define CTRTOL 1.0	/* Tolerance for centroid to move between passes */
+
+/* Find and report on the brightest star; will work with donuts... */
+int psfmoment(int sx, 		/* x offset of pixel 0,0 */
+	     int sy,  		/* y offset of pixel 0,0 */
+	     int binx,  		/* x bin factor */
+	     int biny,   		/* y bin factor */
+/* The "world coords" and FWHM are derived from pixel coords off of the data by
+ *    x_world = *xu = sx + x_data*binx
+ *    y_world = *yu = sy + y_data*binx
+ * Center of the first pixel is (0.5,0.5)
+ */
+	     int bordx,  		/* x border */
+	     int bordy,   		/* y border */
+	     int nx,   			/* x size of image */
+	     int ny,    		/* y size of image */
+	     int NX,   			/* x size of image */
+	     unsigned short *data,	/* ushort image data: 0 = *NO_DATA* */
+	     double *xu,		/* Fitted x position */
+	     double *yu,		/* Fitted y position */
+	     int *fmax,			/* Highest pixel in best bin */
+	     double *fwhm,		/* FWHM of psf (binx,y corrected) */
+	     double *xfwhm,		/* FWHM in x dir (binx corrected) */
+	     double *yfwhm,		/* FWHM in y dir (biny corrected) */
+	     double *bkgnd,		/* Sky level */
+	     double *ftot,		/* Total flux */
+	     double *weight,		/* Composite quality of star: <1 bad,*/
+	     				/*    1-10 so-so, 10-30 OK, >30 fine */
+	     double *snr,		/* S/N of (big-sky)/noise */
+	     int *extra)		/* More return stuff? */
+{
+   int i, j, k, med=0, lq=0, uq=0, bright=0, bright2=0, flim1, flim2;
+   int i0, i1, i2, npix, nbig, pass, biggie, ibig=0, jbig=0;
+   double x0=0.0, y0=0.0, xc=0.0, yc=0.0, a4, a5, a6, mxmy_14, fluxsum, sky;
+   double x1, y1, x2, y2, mx, my, mxy, flux1, flux2, nearlow, r2;
+   double theta, a=0.0;
+   double rms, kappa, kappa2, rcut, flux;
+   int count[65536];
+
+/* Determine the sky level */
+   kappa = 0.99;		/* Fraction of pixels dim enough to ignore */
+   kappa2 = 0.75;		/* Fraction of pixels dim enough to ignore */
+   bzero(count, 65536*sizeof(int));
+   for(j=bordy, flux=0.0; j<ny-bordy; j++) {
+      for(i=bordx; i<nx-bordx; i++) {
+	 (count[data[i+j*NX]])++;
+/* Where's the biggest pixel? */
+	 if(data[i+j*NX] > flux) {
+	    flux = data[i+j*NX];
+	    x0 = i;
+	    y0 = j;
+	 }
+      }
+   }
+   i0 = 0.5*(nx-2*bordx)*(ny-2*bordy);
+   i1 = kappa*(nx-2*bordx)*(ny-2*bordy);
+   i2 = kappa2*(nx-2*bordx)*(ny-2*bordy);
+   for(i=1, j=0; i<65536; i++) {
+      j += count[i];
+      if(j < i0/2) lq = i;
+      if(j < i0) med = i;
+      if(j < (3*i0)/2) uq = i;
+      if(j < i2) bright2 = i;
+      if(j < i1) bright = i;
+   }
+/* Get sky as median between median +/- SIGCLIP*quartile */
+   i0 = MAX(0,med-SIGCLIP*(med-lq));
+   i1 = MIN(65535,med+SIGCLIP*(med-lq));
+   for(i=i0, k=0; i<=i1; i++) k += count[i];
+   if(k == 0) {
+      fprintf(stderr, "psfmoment: No sky pixels at all?\n");
+      return(-1);
+   }
+   for(i=i0, j=0; i<=i1; i++) {
+      j += count[i];
+      if(j >= k/2) break;
+   }
+   sky = i;
+   if(count[i] > 0) sky += ((double)(j-k/2))/count[i] - 1.0;
+
+   rms = (uq-med) / 0.7;
+   if(rms < 0.5) rms = 0.5;
+
+/* subtract sky, clip noise and sum flux and first and moments */
+   flim2 = bright - sky;	/* Flux limit for RMS calculation */
+   flim1 = MIN(2*rms, flim2);	/* Flux limit for centroid calculation */
+   rcut = MAX(binx*nx,biny*ny);	/* Radius limit */
+
+#ifdef TEST
+   printf("i0,i1,lq,med,uq,bright= %d %d %d %d %d %d sky=%.1f rms=%.1f flim1=%d flim2=%d\n", 
+	  i0,i1,lq,med,uq,bright,sky,rms,flim1,flim2);
+#endif
+
+/* Three passes: find center, get decent FWHM, tune it up (if necessary) */
+   for(pass=0; pass<3; pass++) {
+      fluxsum = 0.0;
+      x1 = y1 = flux1 = 0.0;
+      x2 = y2 = mx = my = mxy = flux2 = 0.0;
+      npix = nbig = biggie = 0;
+      nearlow = MAX(nx*nx, ny*ny);
+      for(j=bordy; j<ny-bordy; j++) {
+	 for(i=bordx; i<nx-bordx; i++) {
+	    flux = data[i+j*NX] - sky;
+	    r2 = binx*binx*(i+0.5-x0)*(i+0.5-x0) +
+	         biny*biny*(j+0.5-y0)*(j+0.5-y0);
+/* Where's the nearest pixel which is near sky? */
+	    if(flux < rms && r2 < nearlow) nearlow = r2;
+/* Sum up moments within a radius of rcut */
+	    if(r2 < rcut*rcut) {
+/* Very low flux cut to get total flux */
+	       if(flux > -2*rms) fluxsum += flux;
+/* Modest flux cut to get center */
+	       if(flux > flim1) {
+		  npix++;
+		  flux1 += flux;
+		  x1 += (i+0.5) * flux;
+		  y1 += (j+0.5) * flux;
+	       }
+/* Higher flux cut to get moment */
+	       if(flux > flim2) {
+		  nbig++;
+		  flux2 += flux;
+		  x2 += (i+0.5) * flux;
+		  y2 += (j+0.5) * flux;
+		  mx += (i+0.5)*(i+0.5) * flux;
+		  my += (j+0.5)*(j+0.5) * flux;
+		  mxy += (i+0.5)*(j+0.5) * flux;
+		  if(flux > biggie) {
+		     ibig = i;
+		     jbig = j;
+		     biggie = flux;
+		  }
+	       }
+	    }
+	 }
+      }
+//      printf("npix,xc,yc,mx,my,mxy,fluxsum = %d %.1f %.1f %.1f %.1f %.1f %.1f\n", 
+//	     npix, xc, yc, mx, my, mxy, fluxsum);
+/* Is first moment OK for centroid? */
+      if(flux1 > rms*npix) {
+	 xc = x1 / flux1;
+	 yc = y1 / flux1;
+      } else {
+	 xc = x0;
+	 yc = y0;
+      }
+
+/* Is second moment OK for RMS?  Calculate variances about the mean. */
+      if(flux2 > rms*nbig) {
+	 mx = (mx - x2*x2/flux2) * (binx*binx/flux2);
+	 my = (my - y2*y2/flux2) * (biny*biny/flux2);
+	 mxy = (mxy - x2*y2/flux2) * (binx*biny/flux2);
+      } else {
+	 mx = my = mxy = 0.0;
+      }
+/* Radius from previous center to nearest pixel which is close to sky */
+      nearlow = sqrt(nearlow);
+#ifdef TEST
+      printf("npix,nbig,xc,yc,mx,my,mxy,fluxsum,rcut,nearlow,flim2 = %d %d %5.1f %5.1f %6.1f %6.1f %6.2f %7.0f %5.1f %5.1f %d\n", 
+	     npix, nbig, xc, yc, mx, my, mxy, fluxsum, rcut, nearlow, flim2);
+#endif
+
+      if(mx > 0 && my > 0) {
+	 a4 = sqrt((mx+my)/2.0);
+//	 mxmy_14 = sqrt(sqrt(mx*my));
+//	 a5 = mxy / mxmy_14 ;
+//	 a6 = (mx-my) * mxmy_14 / 2.0 ;
+      } else {
+	 a4 = mxmy_14 = a5 = a6 = 0.0;
+      }
+
+      if(pass > 0 && ABS(x0-xc) < CTRTOL && ABS(y0-yc) < CTRTOL && 
+	 a4 > rcut/5) break;
+
+      x0 = xc;
+      y0 = yc;
+/* Squeeze down rcut to max of 3*RMS or nearlow */
+      rcut = MAX(3*a4, 6.0);
+      rcut = MAX(rcut, nearlow);
+/* Lower flim2 to kappa2 */
+      flim2 = bright2 - sky;
+   }
+
+/* Ellipse variables: major/minor ~ (1+a) */
+   if(a4 > 0) a = sqrt(mxy*mxy+(mx-my)*(mx-my)/4) / ((mx+my)/2);
+   theta = 0.5 * atan2(mxy, (mx-my)/2);
+#ifdef TEST
+   printf("mx,my,mx-my/2,mxy,a, theta = %8.2f %8.2f  %8.2f %8.2f %8.2f %8.2f\n", 
+	  mx,my,(mx-my)/2,mxy,a, theta/0.0174533);
+#endif
+
+/* Return values */
+   *xu = sx + xc*binx;
+   *yu = sy + yc*biny;
+   *fmax = biggie;
+   *fwhm = FWRMS * a4;
+   *xfwhm = FWRMS * sqrt(MAX(0.0, mx));
+   *yfwhm = FWRMS * sqrt(MAX(0.0, my));
+   *bkgnd = sky;
+   *ftot = fluxsum;
+   *snr = a4 > 0 ? fluxsum / sqrt(k*rms*rms) : 0.0;
+/* This could in principle have extra info, e.g. higher order moments */
+   *weight = *snr;
+
+#ifdef TEST
+   printf("xu,yu,FWHM,snr,x,y,max= %.1f %.1f %.2f %.1f %d %d %d\n", 
+	  *xu, *yu, *fwhm, *snr, ibig, jbig, biggie);
+#endif
+
+
+   return(0);
+}
Index: /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/Make.Common
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/Make.Common	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/Make.Common	(revision 23594)
@@ -0,0 +1,1 @@
+link ../Make.Common
Index: /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/burntool/Make.Common
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/burntool/Make.Common	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/burntool/Make.Common	(revision 23594)
@@ -0,0 +1,1 @@
+link ../Make.Common
Index: /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/burntool/Makefile
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/burntool/Makefile	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/burntool/Makefile	(revision 23594)
@@ -0,0 +1,38 @@
+include ../Make.Common
+
+VERSION=0.00
+# CCWARN+=$(WERROR)
+CCDEFS+=$(VERSIONDEFS)
+CCLINK += -lm
+
+# $(EXECNAME): $(OBJS) libfh.a libpsf.a libpscoords.a
+$(EXECNAME): $(OBJS) libfh.a libpscoords.a
+
+include ../Make.Common
+
+# Dependencies by Make.Common $Revision: 2.17 $
+
+$(OBJ)/basement.o: basement.c
+
+$(OBJ)/burnfix.o: burnfix.c burntool.h burnparams.h
+
+$(OBJ)/burntool.o: burntool.c fh/fh.h fhreg/general.h \
+  fhreg/macros.h fhreg/gpc_detector.h \
+  burntool.h burnparams.h
+
+$(OBJ)/burnutils.o: burnutils.c burntool.h burnparams.h
+
+$(OBJ)/cheapfits.o: cheapfits.c
+
+$(OBJ)/persistfix.o: persistfix.c burntool.h burnparams.h
+
+$(OBJ)/persistio.o: persistio.c burntool.h burnparams.h
+
+$(OBJ)/psfstamp.o: psfstamp.c burntool.h burnparams.h \
+  pscoords/pscoords.h
+
+$(OBJ)/psfstats.o: psfstats.c burntool.h psf/psf.h
+
+$(OBJ)/stardetect.o: stardetect.c burntool.h burnparams.h
+
+$(OBJ)/trailfit.o: trailfit.c burntool.h burnparams.h
Index: /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/burntool/burnfix.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/burntool/burnfix.c	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/burntool/burnfix.c	(revision 23594)
@@ -0,0 +1,265 @@
+/* burnfix.c - various functions to identify and fix burn trails */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "math.h"
+
+#include "burntool.h"
+#include "burnparams.h"
+
+/****************************************************************/
+/* burn_fix(): Find and fix all the burned patches in a cell */
+STATIC int burn_fix(int nx, int ny, int NX, int NY, IMTYPE *buf, 
+		    CELL *cell, int cellnum)
+{
+   int k, err;
+
+/* Find all the burned patches and bright stars */
+   err = star_detect(nx, ny, NX, NY, imbuf, mbuf, msbuf, cell, cellnum);
+   err = burn_check(nx, ny, NX, NY, imbuf, mbuf, cell);
+
+/* Expand the masks; burns asymmetrically, stars symmetrically */
+//   err = grow_mask(nx, ny, NX, mbuf, YMASK_GROW, YMASK_GROW, cell->nstar, cell->star);
+//   err = grow_mask(nx, ny, NX, mbuf, XMASK_GROW, YMASK_GROW, cell->nburn, cell->burn);
+   err = grow_mask(nx, ny, NX, mbuf, BMASK_GROW, RMASK_GROW, 
+		   MASK_SAT_HALO, cell->nburn, cell->burn);
+   err = grow_mask(nx, ny, NX, mbuf, BMASK_GROW, RMASK_GROW, 
+		   MASK_STAR_HALO, cell->nstar, cell->star);
+
+/* Fix up all the burns */
+   for(k=0; k<cell->nburn; k++) {
+      if(!cell->burn[k].burned) continue;
+/* Fit the trail */
+      fit_trail(nx, ny, NX, imbuf, mbuf, cell->burn+k, 1, 
+	     cell->sky+cell->bias, cell->rms, BURN_PWR);
+/* Subtract the fit from the image */
+      if(!cell->burn[k].fiterr) sub_fit(nx, ny, NX, buf, cell->burn+k, 1);
+   }
+
+/* For testing, blow away the data in favor of the mask */
+   if(VERBOSE & VERB_MASK) {
+      for(k=0; k<NX*ny; k++) buf[k] = mbuf[k] - BZERO;
+   }
+   return(err);
+}
+
+/****************************************************************/
+/* burn_blab(): Tell us about all the objects found in this cell */
+STATIC int burn_blab(CELL *cell)
+{
+   int i, k, ymid;
+
+/* Tell us about it? */
+   printf("Cell: %d  sky= %d   rms= %d   bias= %d\n", 
+	  cell->cell, cell->sky, cell->rms, cell->bias);
+
+/* The burns */
+   printf("Burns:\n");
+   printf("  #    cx  cy  max     sx  sy    ex  ey  midy  diff  S/B/E  slope     zero\n");
+   for(k=0; k<cell->nburn; k++) {
+      ymid = (cell->burn[k].y0m + cell->burn[k].y1m +
+	      cell->burn[k].y0p + cell->burn[k].y1p + 2) / 4;
+      i = cell->burn[k].nfit / 2;
+      printf("%3d %5d %3d %5d %5d %3d %5d %3d %5d %5d %2d %1d %1d %6.3f %8d\n", 
+	     k, 
+	     cell->burn[k].cx, cell->burn[k].cy,  cell->burn[k].max,
+	     cell->burn[k].sx, cell->burn[k].sy, 
+	     cell->burn[k].ex, cell->burn[k].ey, ymid,
+	     cell->burn[k].diff, cell->burn[k].sat, 
+	     cell->burn[k].burned, cell->burn[k].fiterr,
+	     cell->burn[k].slope, 
+	     cell->burn[k].zero != NULL ? NINT((cell->burn[k]).zero[i]) : 0);
+   }
+
+/* The stars */
+   printf("Stars:\n");
+   printf("  #    cx  cy  max     sx  sy    ex  ey  midy  S/P\n");
+   for(k=0; k<cell->nstar; k++) {
+      ymid = (cell->star[k].y0m + cell->star[k].y1m +
+	      cell->star[k].y0p + cell->star[k].y1p + 2) / 4;
+
+      printf("%3d %5d %3d %5d %5d %3d %5d %3d %5d %2d %1d\n", 
+	     k, cell->star[k].cx, cell->star[k].cy, 
+	     cell->star[k].max, 
+	     cell->star[k].sx, cell->star[k].sy, 
+	     cell->star[k].ex, cell->star[k].ey, ymid,
+	     cell->star[k].sat, cell->star[k].func);
+   }
+   return(0);
+}
+
+/****************************************************************/
+/* burn_restore(): Restore the fitted trails back to the image */
+STATIC int burn_restore(int nx, int ny, int NX, IMTYPE *buf, CELL *cell)
+{
+   int k;
+
+/* Restore all the burns */
+   for(k=0; k<cell->npersist; k++) {
+      if(!cell->persist[k].fiterr) 
+	 sub_fit(nx, ny, NX, buf, &(cell->persist[k]), -1);
+   }
+   return(0);
+}
+
+
+/****************************************************************/
+/* burn_check(): Find all the burned patches in a cell */
+STATIC int burn_check(int nx, int ny, int NX, int NY, DTYPE *data,
+		       MTYPE *mask, CELL *cell)
+{
+   int err, k;
+
+/* Given these big stars, identify which ones really have a burn */
+   for(k=0; k<cell->nburn; k++) {
+      if(!cell->burn[k].burned) {
+	 cell->burn[k].diff = -1;
+	 continue;
+      }
+      err = burn_test(nx, ny, NX, data, cell->rms, mask, cell->burn+k);
+   }
+
+#ifdef TEST
+   printf("Boxes:  sky= %d  rms= %d  bias= %d\n", 
+	  cell->sky, cell->rms, cell->bias);
+   for(k=0; k<cell->nburn; k++) {
+      printf("%5d %5d %5d %5d %5d %5d %5d %5d %5d %5d %5d\n", 
+	     k, cell->burn[k].sx, cell->burn[k].sy, 
+	     cell->burn[k].ex, cell->burn[k].ey, 
+	     cell->burn[k].y0m, cell->burn[k].y1m, cell->burn[k].y0p, cell->burn[k].y0p,
+	     cell->burn[k].diff, cell->burn[k].burned);
+   }
+#endif
+
+/* Make Vista decorations for selected boxes? */
+   if(VERBOSE & VERB_VISTA) vista_marker(cell, VISTAMARKER);
+   return(0);
+}
+
+/****************************************************************/
+/* vista_marker(): Put out Vista commands to mark stars */
+STATIC int vista_marker(CELL *cell, char *fname)
+{
+   int k;
+   FILE *fp;
+
+/* Make Vista decorations for selected boxes? */
+   if(fname != NULL) {
+      if( (fp=fopen(fname, "w")) == NULL) {
+	 fprintf(stderr, "error: cannot open '%s' for writing\n", fname);
+	 return(-1);
+      }
+/* Saturated objects are red (burned) or green (not burned) */
+      for(k=0; k<cell->nburn; k++) {
+	 if(cell->burn[k].burned) fprintf(fp, "itv action=v color=1\n");/*red*/
+	 fprintf(fp, "itv action=b corners=(%d,%d,%d,%d)\n", 
+		 cell->burn[k].sx, cell->burn[k].sy, 
+		 cell->burn[k].ex, cell->burn[k].ey);
+	 fprintf(fp, "itv action=l corners=(%d,%d,%d,%d)\n", 
+		 cell->burn[k].sx, cell->burn[k].y0m, 
+		 cell->burn[k].x0m, cell->burn[k].sy);
+	 fprintf(fp, "itv action=l corners=(%d,%d,%d,%d)\n", 
+		 cell->burn[k].x0p, cell->burn[k].sy, 
+		 cell->burn[k].ex, cell->burn[k].y1m);
+	 fprintf(fp, "itv action=l corners=(%d,%d,%d,%d)\n", 
+		 cell->burn[k].ex, cell->burn[k].y1p, 
+		 cell->burn[k].x1p, cell->burn[k].ey);
+	 fprintf(fp, "itv action=l corners=(%d,%d,%d,%d)\n", 
+		 cell->burn[k].x1m, cell->burn[k].ey, 
+		 cell->burn[k].sx, cell->burn[k].y0p);
+	 fprintf(fp, "itv action=v color=0\n");/*green*/
+      }
+/* Stars are white */
+      fprintf(fp, "itv action=v color=3\n");/*white*/
+      for(k=0; k<cell->nstar; k++) {
+	 fprintf(fp, "itv action=b corners=(%d,%d,%d,%d)\n", 
+		 cell->star[k].sx, cell->star[k].sy, 
+		 cell->star[k].ex, cell->star[k].ey);
+	 fprintf(fp, "itv action=l corners=(%d,%d,%d,%d)\n", 
+		 cell->star[k].sx, cell->star[k].y0m, 
+		 cell->star[k].x0m, cell->star[k].sy);
+	 fprintf(fp, "itv action=l corners=(%d,%d,%d,%d)\n", 
+		 cell->star[k].x0p, cell->star[k].sy, 
+		 cell->star[k].ex, cell->star[k].y1m);
+	 fprintf(fp, "itv action=l corners=(%d,%d,%d,%d)\n", 
+		 cell->star[k].ex, cell->star[k].y1p, 
+		 cell->star[k].x1p, cell->star[k].ey);
+	 fprintf(fp, "itv action=l corners=(%d,%d,%d,%d)\n", 
+		 cell->star[k].x1m, cell->star[k].ey, 
+		 cell->star[k].sx, cell->star[k].y0p);
+      }
+      fprintf(fp, "itv action=v color=0\n");/*green*/
+      fclose(fp);
+   }
+   return(0);
+}
+
+/****************************************************************/
+/* burn_test() tests whether a box is burned or not */
+STATIC int burn_test(int nx, int ny, int NX, DTYPE *data, int rms,
+		       MTYPE *mask, OBJBOX *box)
+{
+   int i, j, n, nrow, nmed;
+   int y0, y1, ymid, xsum, dx=0;
+
+/* FIXME: needs a test for flat-toppedness as well as trail... */
+/* FIXME: needs a test for pure, massive saturation */
+
+/* Center line */
+   ymid = (box->y0m + box->y0p + box->y1m + box->y1p + 2) / 4;
+/* Starting point offset */
+   y0 = MAX(ymid-box->sy, box->ey-ymid);
+   if(y0 < FIT_EDGE) y0 = FIT_EDGE;
+
+/* Irrelevant, too near the top */
+   if(box->ey > ny-FIT_EDGE-3) {
+      box->diff = -1;
+      box->burned = -1;
+/* But looks like a bad one which will leave behind persistence */
+      if(box->ey-box->sy+1 > 4) box->burned = 1;
+      return(0);
+
+/* Need a one-sided test */
+   } else if(box->sy < FIT_EDGE+3) {
+/* Ending point offset */
+      y1 = ny;
+      dx = box->ex - box->sx;
+      if(box->sx > nx-1-box->ex) dx *= -1;
+
+   } else {
+/* Ending point offset */
+      y1 = MIN(ny-2-ymid, ymid-1);
+   }
+   n = MIN(100, y1-y0);
+
+   nmed = 0;
+   for(j=0; j<n; j++) {
+      xsum = 0;
+      nrow = 0;
+      for(i=box->sx; i<=box->ex; i++) {
+	 xsum += data[i+(ymid+y0+j)*NX]*(mask[i+(ymid+y0+j)*NX] == MASK_NONE);
+	 if(y1 < ny) {
+	    xsum -= data[i+(ymid-y0-j)*NX] * 
+	       (mask[i+(ymid-y0-j)*NX] == MASK_NONE);
+	    nrow += (mask[i+(ymid-y0-j)*NX] == MASK_NONE) * 
+	       (mask[i+(ymid+y0+j)*NX] == MASK_NONE);
+	 } else {
+	    xsum -= data[i+dx+(ymid+y0+j)*NX] * 
+	       (mask[i+dx+(ymid+y0+j)*NX] == MASK_NONE);
+	    nrow += (mask[i+dx+(ymid+y0+j)*NX] == MASK_NONE) * 
+	       (mask[i+(ymid+y0+j)*NX] == MASK_NONE);
+	 }
+      }
+      if(nrow > 0) median_buf[nmed++] = xsum / nrow;
+   }
+   box->diff = int_median(nmed, median_buf);
+   box->burned = 0;
+   if(box->diff > 0.3*rms) box->burned = 1;
+   return(0);
+}
Index: /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/burntool/burnparams.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/burntool/burnparams.h	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/burntool/burnparams.h	(revision 23594)
@@ -0,0 +1,51 @@
+/* A global buffer into which routines can accumulate detections */
+
+/* '#define EXTERN' in just one file, burntool.c, to declare variables */
+#ifndef EXTERN
+#define EXTERN extern
+#endif
+
+#define MAXBURN 10000
+EXTERN OBJBOX boxbuf[MAXBURN];
+
+EXTERN int *median_buf;		/* Generic buffer for integer medians */
+EXTERN int nmedian_buf;
+
+EXTERN MTYPE *mbuf;		/* Buffer for object mask */
+EXTERN int nmbuf;
+
+EXTERN MTYPE *msbuf;		/* Buffer for vetoed star mask */
+EXTERN int nmsbuf;
+
+EXTERN DTYPE *imbuf;		/* Copy of cell data w/o BZERO */
+EXTERN int nimbuf;
+
+EXTERN int VERBOSE;		/* Verbosity level */
+
+EXTERN int BZERO;
+EXTERN int USHORT_BIAS;		/* Bias level restored to ushort stamps */
+
+EXTERN int MAX_READ_NOISE;	/* Maximum believable read noise (ADU) */
+EXTERN double MIN_EADU;		/* Minimum believable e/ADU */
+
+EXTERN int BURN_THRESH  ;	/* Threshold for onset of burning */
+EXTERN int TRAIL_THRESH ;	/* Trailing might go this low */
+EXTERN int MAX_THRESH   ;	/* Possibly trailing stars? */
+EXTERN int STAR_THRESH  ;	/* Threshold for star above sky */
+EXTERN double STAR_FRAC ;	/* Fraction to follow star profile */
+EXTERN int PSF_THRESH   ;	/* Threshold for a star to be a PSF */
+
+//EXTERN double XMASK_GROW ;	/* Growth of burned boxes in x dir */
+//EXTERN double YMASK_GROW ;	/* Growth of burn/star in y dir */
+
+EXTERN double BMASK_GROW ;	/* Growth of burned boxes in size */
+EXTERN double RMASK_GROW ;	/* Growth of burn/star in diameter */
+
+EXTERN int MIN_PSF_SIZE  ;	/* Min box size for stamp selection */
+EXTERN int PSF_CTR_TOL   ;	/* Choose max or box ctr for stamp */
+EXTERN int CONCAT_FITS;		/* Write concat FITS for PSF? (else 3D) */
+EXTERN int MAX_PSF_PER_CELL ; 	/* Max number of PSF stars accepted per cell */
+
+EXTERN double NEGLIGIBLE_TRAIL;	/* Don't sweat less than this * sigma */
+
+
Index: /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/burntool/burntool.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/burntool/burntool.c	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/burntool/burntool.c	(revision 23594)
@@ -0,0 +1,688 @@
+/*
+ * burntool.c - identify and remove persistance streaks from a MEF.
+ * Syntax: burntool mef_file [options]
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "math.h"
+#include "fh/fh.h"
+#include "fhreg/general.h"
+#include "fhreg/gpc_detector.h"
+
+#include "burntool.h"
+#define EXTERN  /* Define EXTERN to declare variables in params.h */
+#include "burnparams.h"
+
+int
+main(int argc, const char* argv[])
+{
+   int i, j, k, nx, ny, err;
+   HeaderUnit ihu = fh_create();
+   HeaderUnit ehu;
+   const char* ifilename = "-";
+   const char* ofilename = NULL;
+   char extname[FH_MAX_STRLEN+1], otaposn[FH_MAX_STRLEN+1];
+   int cellmask[MAXCELL];
+   int otanum;
+   int nextend, cellxy, cell, cellcode;
+   int ext, update, restore, psfsize, psfavg;
+   int outfd;
+   IMTYPE *buf;
+   const char *burnfile=NULL,  *persistfile=NULL;
+   const char *psffile=NULL, *psfstatfile=NULL;
+   CELL OTA[MAXCELL];	/* Cell structure for entire OTA */
+
+   if (argc == 1)  {
+     syntax(argv[0]);
+     exit(EXIT_SUCCESS);
+   }
+
+   if(argc > 1 && strncmp(argv[1], "help", 4) == 0) {
+      syntax(argv[0]);
+      exit(EXIT_SUCCESS);
+   }
+
+   if(argc > 1 && strncmp(argv[1], "--help", 6) == 0) {
+      syntax(argv[0]);
+      exit(EXIT_SUCCESS);
+   }
+
+   if(argc > 1 && strncmp(argv[1], "-h", 6) == 0) {
+      syntax(argv[0]);
+      exit(EXIT_SUCCESS);
+   }
+
+   if(argv[1]) ifilename = argv[1];
+
+   if(fh_file(ihu, ifilename, FH_FILE_RDWR) != FH_SUCCESS) {
+      fprintf(stderr, "\rerror: burntool could not open file `%s'\n",
+	      ifilename);
+      exit(EXIT_FAILURE);
+   }
+   nextend = fh_extensions(ihu);
+   if (nextend < 1) {
+      fprintf(stderr, "\rerror: `%s' is not a multi-extension FITS\n",
+	      ifilename);
+      exit(EXIT_FAILURE);
+   }
+
+/* Initialize the globals */
+   VERBOSE = 0;			/* Verbosity level */
+
+   median_buf=NULL;		/* Buffer for medians */
+   nmedian_buf=0;
+
+   mbuf=NULL;			/* Mask buffer */
+   nmbuf=0;
+
+   imbuf=NULL;			/* Image copy buffer */
+   nimbuf=0;
+
+   BZERO = 32768;		/* BZERO for input data */
+   USHORT_BIAS = 1000;		/* Bias level restored to ushort stamps */
+
+   MAX_READ_NOISE = 20;		/* Maximum believable read noise (ADU) */
+   MIN_EADU = 0.3;		/* Minimum believable e/ADU */
+
+   BURN_THRESH  = 30000;	/* Threshold for onset of burning */
+   TRAIL_THRESH = 10000;	/* Trailing might go this low */
+   MAX_THRESH   = 20000;	/* Possibly trailing stars? */
+   STAR_THRESH  =  1000;	/* Threshold for star above sky */
+   STAR_FRAC =  0.3;		/* Fraction to follow star profile */
+   PSF_THRESH   =  5000;	/* Threshold for a star to be a PSF */
+
+   BMASK_GROW =  5.0;		/* Growth of burned boxes in size */
+   RMASK_GROW =  3.0;		/* Growth of burn/star in diameter */
+
+   MIN_PSF_SIZE  =    3;	/* Min box size for stamp selection */
+   PSF_CTR_TOL   =    8;	/* Choose max or box ctr for stamp */
+   CONCAT_FITS = 1;		/* Write concat FITS for PSF? (else 3D) */
+   MAX_PSF_PER_CELL = 20; 	/* Max number of PSF stars accepted per cell */
+
+   NEGLIGIBLE_TRAIL = 0.5;	/* Don't sweat less than this * sigma */
+
+
+/* Parse the args */
+   cellxy = -1;
+   update = 1;
+   restore = 0;
+   psfsize = 32;
+   psfavg = 0;
+   for(i=0; i<MAXCELL; i++) cellmask[i] = 1;
+   for(i=2; i<argc; i++) {
+
+/* Work on just one cell?  xy ID mode. */
+      if(strncmp(argv[i], "xy=", 3) == 0) {		/* xy=CELL_ID */
+	 if(sscanf(argv[i]+3, "%d", &cellxy) != 1) {
+	    fprintf(stderr, "\rerror: cannot get cell ID from `%s'\n", argv[i]);
+	    exit(EXIT_FAILURE);
+	 }
+
+/* Work on just one cell? Cell count [0:63] mode. */
+      } else if(strncmp(argv[i], "cell=", 5) == 0) {	/* cell=0:63 */
+	 if(sscanf(argv[i]+5, "%d", &cellxy) != 1) {
+	    fprintf(stderr, "\rerror: cannot get cell ID from `%s'\n", argv[i]);
+	    exit(EXIT_FAILURE);
+	 }
+	 cellxy = cellxy/8 + 10*(cellxy%8);
+
+/* Veto cells by a 64 digit mask (0/1 for cells 0-63) */
+      } else if(strncmp(argv[i], "mask=", 5) == 0) {	/* mask=64_digits */
+	 if(strlen(argv[i]) < 5+MAXCELL) {
+	    fprintf(stderr, "\rerror: cannot get mask from `%s'\n", argv[i]);
+	    exit(EXIT_FAILURE);
+	 } else {
+	    for(j=0; j<MAXCELL; j++) cellmask[j] = argv[i][5+j] != '0';
+	 }
+
+/* Modify the input MEF by subtracting fits? */
+      } else if(strncmp(argv[i], "update=", 7) == 0) {	/* update={t|f} */
+	 update = argv[i][7] == 'y' || argv[i][7] == '1' || argv[i][7] == 't';
+
+/* Modify the input MEF by subtracting fits? */
+      } else if(strncmp(argv[i], "restore=", 8) == 0) {	/* restore={t|f} */
+	 restore = argv[i][8] == 'y' || argv[i][8] == '1' || argv[i][8] == 't';
+
+/* Output file for burn streaks */
+      } else if(strncmp(argv[i], "trailout=", 9) == 0) {/* trailout=fname */
+	 burnfile = argv[i] + 9;
+
+/* Output file for burn streaks */
+      } else if(strncmp(argv[i], "out=", 4) == 0) {	/* out=fname */
+	 burnfile = argv[i] + 4;
+
+# if (0)
+/* XXX disable for now: is not yet working */
+/* Alternate Output file for deburned image */
+      } else if(strncmp(argv[i], "outimage=", 9) == 0) {	/* outimage=fname */
+	ofilename = argv[i] + 9;
+# endif
+
+/* Input file for previous burn persistence streaks */
+      } else if(strncmp(argv[i], "trailin=", 8) == 0) {	/* trailin=fname */
+	 persistfile = argv[i] + 8;
+
+/* Input file for previous burn persistence streaks */
+      } else if(strncmp(argv[i], "in=", 3) == 0) {	/* in=fname */
+	 persistfile = argv[i] + 3;
+
+/* Output file for PSF gallery */
+      } else if(strncmp(argv[i], "psf=", 4) == 0) {	/* psf=fname */
+	 psffile = argv[i] + 4;
+
+/* Output file for PSF stats on each cell */
+      } else if(strncmp(argv[i], "psfstat=", 8) == 0) {	/* psfstat=fname */
+	 psfstatfile = argv[i] + 8;
+
+/* How many cells to average PSF stats over? (2^N) */
+      } else if(strncmp(argv[i], "psfavg=", 7) == 0) {	/* psfavg=N */
+	 if(sscanf(argv[i]+7, "%d", &psfavg) != 1) {
+	    fprintf(stderr, "\rerror: cannot get psf avg from `%s'\n", argv[i]);
+	    exit(EXIT_FAILURE);
+	 }
+
+/* How big a box to use for PSF extraction? */
+      } else if(strncmp(argv[i], "psfsize=", 8) == 0) {	/* psfsize=size */
+	 if(sscanf(argv[i]+8, "%d", &psfsize) != 1) {
+	    fprintf(stderr, "\rerror: cannot get psf size from `%s'\n", argv[i]);
+	    exit(EXIT_FAILURE);
+	 }
+
+/* How big a box to use for PSF extraction? */
+      } else if(strncmp(argv[i], "psfctr=", 7) == 0) {	/* psfsize=size */
+	 if(sscanf(argv[i]+7, "%d", &PSF_CTR_TOL) != 1) {
+	    fprintf(stderr, "\rerror: cannot get psf decenter from `%s'\n", argv[i]);
+	    exit(EXIT_FAILURE);
+	 }
+
+/* How big must a box be to qualify for PSF extraction? */
+      } else if(strncmp(argv[i], "psfmin=", 7) == 0) {	/* psfmin=size */
+	 if(sscanf(argv[i]+7, "%d", &MIN_PSF_SIZE) != 1) {
+	    fprintf(stderr, "\rerror: cannot get psf size from `%s'\n", argv[i]);
+	    exit(EXIT_FAILURE);
+	 }
+
+/* How big must a box be to qualify for PSF extraction? */
+      } else if(strncmp(argv[i], "psfmaxn=", 8) == 0) {	/* psfmaxn=N */
+	 if(sscanf(argv[i]+8, "%d", &MAX_PSF_PER_CELL) != 1) {
+	    fprintf(stderr, "\rerror: cannot get psf limit from `%s'\n", argv[i]);
+	    exit(EXIT_FAILURE);
+	 }
+
+/* Thresholds */
+      } else if(strncmp(argv[i], "thrburn=", 8) == 0) {	/* thrburn=thresh */
+	 if(sscanf(argv[i]+8, "%d", &BURN_THRESH) != 1) {
+	    fprintf(stderr, "\rerror: cannot get value from `%s'\n", argv[i]);
+	    exit(EXIT_FAILURE);
+	 }
+
+      } else if(strncmp(argv[i], "thrtrail=", 9) == 0) {/* thrtrail=thresh */
+	 if(sscanf(argv[i]+9, "%d", &TRAIL_THRESH) != 1) {
+	    fprintf(stderr, "\rerror: cannot get value from `%s'\n", argv[i]);
+	    exit(EXIT_FAILURE);
+	 }
+
+      } else if(strncmp(argv[i], "thrmax=", 7) == 0) {	/* thrmax=thresh */
+	 if(sscanf(argv[i]+7, "%d", &MAX_THRESH) != 1) {
+	    fprintf(stderr, "\rerror: cannot get value from `%s'\n", argv[i]);
+	    exit(EXIT_FAILURE);
+	 }
+
+      } else if(strncmp(argv[i], "thrstar=", 8) == 0) {	/* thrstar=thresh */
+	 if(sscanf(argv[i]+8, "%d", &STAR_THRESH) != 1) {
+	    fprintf(stderr, "\rerror: cannot get value from `%s'\n", argv[i]);
+	    exit(EXIT_FAILURE);
+	 }
+
+      } else if(strncmp(argv[i], "thrpsf=", 7) == 0) {	/* thrpsf=thresh */
+	 if(sscanf(argv[i]+7, "%d", &PSF_THRESH) != 1) {
+	    fprintf(stderr, "\rerror: cannot get value from `%s'\n", argv[i]);
+	    exit(EXIT_FAILURE);
+	 }
+
+/* Star skirt fraction */
+      } else if(strncmp(argv[i], "fracstar=", 9) == 0) {/* fracstar=skirt */
+	 if(sscanf(argv[i]+9, "%lf", &STAR_FRAC) != 1) {
+	    fprintf(stderr, "\rerror: cannot get value from `%s'\n", argv[i]);
+	    exit(EXIT_FAILURE);
+	 }
+
+/* Mask growth fraction in diameter */
+///* Mask growth fraction in x direction */
+//      } else if(strncmp(argv[i], "xmask=", 6) == 0) {	/* xmask=factor */
+//	 if(sscanf(argv[i]+6, "%lf", &XMASK_GROW) != 1) {
+      } else if(strncmp(argv[i], "rmask=", 6) == 0) {	/* rmask=factor */
+	 if(sscanf(argv[i]+6, "%lf", &RMASK_GROW) != 1) {
+	    fprintf(stderr, "\rerror: cannot get value from `%s'\n", argv[i]);
+	    exit(EXIT_FAILURE);
+	 }
+
+///* Mask growth fraction in y direction */
+//      } else if(strncmp(argv[i], "ymask=", 6) == 0) {	/* ymask=factor */
+//	 if(sscanf(argv[i]+6, "%lf", &YMASK_GROW) != 1) {
+/* Mask growth fraction in box size */
+      } else if(strncmp(argv[i], "bmask=", 6) == 0) {	/* bmask=factor */
+	 if(sscanf(argv[i]+6, "%lf", &BMASK_GROW) != 1) {
+	    fprintf(stderr, "\rerror: cannot get value from `%s'\n", argv[i]);
+	    exit(EXIT_FAILURE);
+	 }
+
+/* Concatenated FITS? */
+      } else if(strncmp(argv[i], "psf3dfits=", 10) == 0) {/* concat FITS? */
+	 CONCAT_FITS = argv[i][10] == 'n' || argv[i][10] == '0' || 
+	    argv[i][10] == 'f';
+
+/* Quiet? */
+      } else if(strncmp(argv[i], "quiet=", 6) == 0) {	/* quiet={t|f} */
+	 if(argv[i][6] == 'y' || argv[i][6] == '1' || argv[i][6] == 't') {
+	    VERBOSE = 0;
+	 } else {
+	    VERBOSE = 1;
+	 }
+
+/* Verbosity level? */
+      } else if(strncmp(argv[i], "verbose=", 8) == 0) {	/* verbose=N */
+	 if(sscanf(argv[i]+8, "%i", &VERBOSE) != 1) {
+	    fprintf(stderr, "\rerror: cannot get value from `%s'\n", argv[i]);
+	    exit(EXIT_FAILURE);
+	 }
+
+      } else if(strncmp(argv[i], "help", 4) == 0) {	/* help output */
+	 syntax(argv[0]);
+	 exit(EXIT_SUCCESS);
+
+      } else {
+	 fprintf(stderr, "\rerror: unrecognized option `%s'\n", argv[i]);
+	 syntax(argv[0]);
+	 exit(EXIT_FAILURE);
+      }
+   }
+
+   if(restore && persistfile == NULL) {
+      fprintf(stderr, "\rerror: must specify an input file for restore\n");
+      exit(EXIT_FAILURE);
+   }
+
+/* Inititialize cell structure */
+   for(cell=0; cell<MAXCELL; cell++) {
+      OTA[cell].cell = cell;
+      OTA[cell].nburn = OTA[cell].nstar = OTA[cell].npersist = 0;
+      OTA[cell].bias = OTA[cell].sky = OTA[cell].rms = OTA[cell].time = 0;
+   }
+
+/* Read the persistence data for this OTA */
+   if(persistfile != NULL) {
+      if(persist_read(OTA, persistfile)) exit(EXIT_FAILURE);
+   }
+
+/* Which OTA is this??? */
+   if(fh_get_str(ihu, "FPPOS", otaposn, sizeof(otaposn)) != FH_SUCCESS || 
+      strlen(otaposn) != 4) {
+      fprintf(stderr, "logonly: unrecognized fppos\n");
+      otanum = -1;
+
+   } else {
+      otanum = (otaposn[2] - '0') + 8*(otaposn[3] - '0');
+   }
+
+   if (ofilename) {
+     outfd = creat (ofilename, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
+     if (outfd == -1) {
+	 fprintf(stderr, "\rerror: Cannot open %s for output\n", ofilename);
+         exit(EXIT_FAILURE);
+      }
+
+     if (fh_write(ihu, outfd)) {
+	 fprintf(stderr, "\rerror: Trouble writing PHU to %s\n", ofilename);
+         exit(EXIT_FAILURE);
+      }
+   }
+
+/* Look at all the MEF's extensions */
+   for (ext = 1; ext <= nextend; ext++) {
+      int naxis, naxis1, naxis2, naxis3;
+      int prescan1, ovrscan1, ovrscan2, pontime;
+      double bzero_d;
+      char xtension[FH_MAX_STRLEN + 1];
+
+      if (!(ehu = fh_ehu(ihu, ext)) || 
+	  fh_get_str(ehu, "EXTNAME", extname, sizeof(extname)) != FH_SUCCESS) {
+	 fprintf(stderr,
+                 "\rerror: Cannot read EXTNAME from `%s' for extension #%d\n",
+                 ifilename, ext);
+         exit(EXIT_FAILURE);
+      }
+      if(fh_get_str(ehu, "XTENSION", xtension, sizeof(xtension)) != FH_SUCCESS) {
+	 fprintf(stderr,
+		 "\rerror: Cannot read XTENSION from `%s' for extension #%d\n",
+		 ifilename, ext);
+	 exit(EXIT_FAILURE);	 
+      }
+      if(!strcmp("TABLE", xtension)) {
+	 if(VERBOSE > 0) {
+	    fprintf(stderr,
+		    "logonly: burntool skipping table extension %s\n", extname); 
+	 }
+	 continue;
+      }
+      if ((extname[0] != 'x' || extname[1] != 'y') &&
+          (extname[0] != 'x' || extname[2] != 'y')) {
+         fprintf(stderr,
+                 "warning: Skipping non-image extension %s\n", extname);
+/*
+ * %%% When reading from a pipe, does the data still need
+ * to get read in?
+ */
+         continue;
+      }
+      cellcode = -1;
+      sscanf(extname+2, "%d", &cellcode);
+      cell = (cellcode / 10) + 8*(cellcode % 10);
+      if(cell < 0 || cell > MAXCELL-1) {
+         fprintf(stderr,
+                 "\rerror: Illegal cell number %d from '%s'\n", cell, extname);
+	 exit(EXIT_FAILURE);
+      }
+
+      if((cellxy >= 0 && cellcode != cellxy) || !cellmask[cell]) {
+	 if(VERBOSE > 0) {
+	    fprintf(stderr,
+		    "logonly: Skipping non-requested extension %s\n", extname);
+	 }
+	 continue;
+      }
+
+/* Get PONTIME as a counter for any burn's persistence */
+      if(fh_get_int(ehu, "PONTIME", &pontime) != FH_SUCCESS) {
+	 fprintf(stderr, "logonly: cannot read PONTIME\n");
+      }
+      OTA[cell].time = pontime;
+
+      naxis3 = 1;
+      if (fh_get_PRESCAN1(ehu, &prescan1) != FH_SUCCESS ||
+	  fh_get_OVRSCAN1(ehu, &ovrscan1) != FH_SUCCESS ||
+	  fh_get_OVRSCAN2(ehu, &ovrscan2) != FH_SUCCESS ||
+	  fh_get_NAXIS(ehu, &naxis) != FH_SUCCESS ||
+          fh_get_NAXIS1(ehu, &naxis1) != FH_SUCCESS ||
+          fh_get_NAXIS2(ehu, &naxis2) != FH_SUCCESS ||
+          (naxis >= 3 && fh_get_NAXIS3(ehu, &naxis3) != FH_SUCCESS)) {
+         fprintf(stderr, "\rerror: Cannot get NAXIS*'\n");
+         exit(EXIT_FAILURE);
+      }
+      if (naxis != 2) {
+
+/* If this extension looks like video, just skip over it
+ * with a little mention that it was ignored and allow all 
+ * of the other extensions to be processed. This will get 
+ * faked out if we ever provide a MEF with a 2-frame video
+ * extension in it, but that should be pretty unlikely. */
+	 if((naxis == 3) && (naxis3 > 2)) {
+	    if(VERBOSE > 0) {
+	       fprintf(stderr, "logonly: burntool skipping 3D, %d frame (video?) extension %s.\n", naxis3, extname);
+	    }
+	    continue;
+	 }
+	 fprintf(stderr, "\rerror: 32bpp support not yet implemented in burntool.\n");
+	 exit(EXIT_FAILURE);
+      }
+/* Check BSCALE and warn if it is anything other than 1.0.
+ * This program does not handle applying a BSCALE, nor
+ * does it handle anything other than 16-bit input at the
+ * moment.  (It is intended only to manipulate raw OTA data. */
+      {
+	 double bscale_d;
+	 if (fh_get_BSCALE(ehu, &bscale_d) == FH_SUCCESS &&
+	     bscale_d != 1.0)
+	 {
+	    fprintf(stderr, "warning: Ignoring BSCALE value of %f!\n", bscale_d);
+	 }
+      }
+/* Trust whatever is in the FITS header for BZERO. */
+      if (fh_get_BZERO(ehu, &bzero_d) == FH_SUCCESS) BZERO = bzero_d;
+
+      buf = (IMTYPE*)malloc(naxis1*naxis2*naxis3*sizeof(short));
+      if (fh_read_padded_image(ehu, fh_file_desc(ehu), buf,
+			       naxis1*naxis2*naxis3*sizeof(short),
+			       FH_TYPESIZE_16) != FH_SUCCESS) {
+	 fprintf(stderr, "\rerror: failed to read image data for extension `%s'.\n",
+		 extname);
+	 free(buf);
+	 exit(EXIT_FAILURE);
+      }
+
+      {
+
+	 nx = naxis1 - ovrscan1;
+	 ny = naxis2 - ovrscan2;
+
+/* Check for space... */
+	 mem_init(nx, ny, naxis1, naxis2);
+
+/* Copy this cell's data */
+	 for(k=0; k<naxis1*naxis2; k++) imbuf[k] = buf[k] + BZERO;
+
+/* Get bias and sky levels */
+	 err = cell_stats(nx, ny, naxis1, naxis2, imbuf, OTA+cell);
+
+/* Does this cell look kosher? */
+	 if(OTA[cell].rms*OTA[cell].rms > 
+	    OTA[cell].sky/MIN_EADU + MAX_READ_NOISE*MAX_READ_NOISE) {
+	    if(VERBOSE > 0) {
+	       fprintf(stderr, "logonly: cell %d is unreasonably noisy, skipping...\n", cell);
+	    }
+	    continue;
+	 }
+
+
+	 if(!restore) {
+
+/* Fix up the burns */
+	    burn_fix(naxis1-ovrscan1, naxis2-ovrscan2, naxis1, naxis2, buf, 
+		     OTA+cell, cell);
+
+/* Collect up a good star list */
+	    if(psffile != NULL || psfstatfile != NULL) {
+	       psf_select(naxis1-ovrscan1, naxis2-ovrscan2, naxis1, 
+			  mbuf, imbuf, OTA[cell].nstar, OTA[cell].star, 
+			  psfsize, OTA[cell].sky+OTA[cell].bias);
+	    }
+
+/* Tell us about it? */
+	    if(VERBOSE & VERB_NORM) burn_blab(OTA+cell);
+
+/* Fix up the streaks */
+	    persist_fix(naxis1-ovrscan1, naxis2-ovrscan2, naxis1, buf, 
+			OTA+cell);
+
+/* Tell us about it? */
+	    if(VERBOSE & VERB_NORM) persist_blab(OTA+cell);
+
+	 } else {
+
+/* Restore the old burns */
+	    burn_restore(naxis1-ovrscan1, naxis2-ovrscan2, naxis1, 
+			 buf, OTA+cell);
+	 }
+
+/* Write the corrected data back to the FITS. */
+	 if(update) {
+	   if (ofilename) {
+	     fh_ehu(ehu, 0);	/* Seek back to the start of data */
+
+	     if (fh_write(ehu, outfd)) {
+	       fprintf(stderr, "\rerror: Trouble writing EXT %d to %s\n", ext, ofilename);
+	       exit(EXIT_FAILURE);
+	     }
+
+	     if (fh_write_padded_image(ehu, outfd, buf,
+				       naxis1*naxis2*naxis3*sizeof(short),
+				       FH_TYPESIZE_16) != FH_SUCCESS) {
+	       fprintf(stderr, "\rerror: failed to re-write image data for extension `%s'.\n",
+		       extname);
+	       free(buf);
+	       exit(EXIT_FAILURE);
+	     }
+	   } else {
+	     fh_ehu(ehu, 0);	/* Seek back to the start of data */
+	     if (fh_write_padded_image(ehu, fh_file_desc(ehu), buf,
+				       naxis1*naxis2*naxis3*sizeof(short),
+				       FH_TYPESIZE_16) != FH_SUCCESS) {
+	       fprintf(stderr, "\rerror: failed to re-write image data for extension `%s'.\n",
+		       extname);
+	       free(buf);
+	       exit(EXIT_FAILURE);
+	     }
+	   }
+	 }
+      }
+      
+      free(buf);
+   }
+
+   if (ofilename) {
+     if (close(outfd)) {
+       fprintf(stderr, "\rerror: trouble writing data to output file %s\n", ofilename);
+       exit(EXIT_FAILURE);
+     }
+   }
+      
+/* Dump out the postage stamp file */
+   if(psffile != NULL) {
+      psf_write(psfsize, psfsize, OTA, otanum, psffile);
+   }
+
+/* Dump out the PSF stats */
+   if(psfstatfile != NULL) {
+      psf_write_stats(psfsize, psfsize, OTA, otanum, psfstatfile, psfavg);
+   }
+
+   fh_destroy(ihu);
+
+/* Write the persistence data for the next image */
+   if(burnfile != NULL) persist_write(OTA, burnfile);
+
+   exit(EXIT_SUCCESS);
+}
+
+/****************************************************************/
+/* mem_init(): Allocate space for various functions */
+STATIC int mem_init(int nx, int ny, int NX, int NY)
+{
+/* Make some space for medians */
+   if(2*SKY_MARG*ny > nmedian_buf) {
+      if(median_buf != NULL) free(median_buf);
+      median_buf = (int *)calloc(2*SKY_MARG*ny, sizeof(int));
+      nmedian_buf = 2*SKY_MARG*ny;
+   }
+
+/* Make some space for cell copy */
+   if(NX*NY > nimbuf) {
+      if(imbuf != NULL) free(imbuf);
+      imbuf = (int *)calloc(NX*NY, sizeof(DTYPE));
+      nimbuf = NX*NY;
+   }
+
+/* Make some space for masks */
+   if(NX*NY > nmbuf) {
+      if(mbuf != NULL) free(mbuf);
+      mbuf = (int *)calloc(NX*NY, sizeof(MTYPE));
+      nmbuf = NX*NY;
+   }
+
+/* Make some space for star veto masks */
+   if(NX*NY > nmsbuf) {
+      if(msbuf != NULL) free(msbuf);
+      msbuf = (int *)calloc(NX*NY, sizeof(MTYPE));
+      nmsbuf = NX*NY;
+   }
+   return(0);
+}
+
+
+#define Q_CLIP 3.0
+/****************************************************************/
+/* cell_stats(): Get bias, sky and noise levels */
+STATIC int cell_stats(int nx, int ny, int NX, int NY, DTYPE *data,
+		      CELL *cell)
+{
+   int i, j, k, n;
+
+/* Get bias stats */
+   if(nx == NX) {
+      cell->bias = 0;
+   } else {
+      for(j=1; j<ny-1; j++) median_buf[j-1] = data[nx+(NX-nx)/2+j*NX];
+      cell->bias = int_median(ny-2, median_buf);
+   }
+
+/* Get sky stats */
+   for(k=n=0; k<nx*ny; k+=((617*nx)/1000)) {
+      i = k % nx;
+      j = k / nx;
+      median_buf[n++] = data[i+NX*j];
+   }
+/* First pass at sky and quartile */
+   cell->sky = int_median(n, median_buf);
+   cell->rms = 1.33*(cell->sky - median_buf[n/4]);
+
+/* Clip at 3 sigma */
+   for(j=0; median_buf[j] < cell->sky - Q_CLIP*cell->rms && j<n/2; j++);
+   for(k=n-1; median_buf[k] > cell->sky + Q_CLIP*cell->rms && k>n/2; k--);
+//   printf("%5d %5d %5d %5d %5d", cell->sky, cell->rms, n, j, k);
+   cell->sky = median_buf[(j+k)/2];
+   cell->rms = 1.33 * (cell->sky - median_buf[(3*j+k)/4]);
+//   printf("%5d %5d\n", cell->sky, cell->rms);
+   cell->sky -= cell->bias;
+
+   return(0);
+}
+
+
+STATIC void syntax(const char *prog)
+{
+   printf("Syntax: %s mef_file [options]\n", prog);
+   printf("   where options include:\n");
+   printf(" xy=xy          Work on just one cell?  xy ID mode.\n");
+   printf(" cell=N         Work on just one cell? Cell count [0:63] mode.\n");
+   printf(" mask=0101...   64 digits to work on cells 0:63.\n");
+   printf(" update={t|f}   Modify the input MEF by subtracting fits?\n");
+   printf(" restore={t|f}  Restore the input MEF by adding input fits?\n");
+   printf(" in=fname       Input file for previous burn persistence streaks\n");
+   printf(" out=fname      Output file for burn streaks\n");
+   printf(" outimage=imname Output file for MEF image\n");
+//   printf(" trailin=fname  Input file for previous burn persistence streaks\n");
+//   printf(" trailout=fname Output file for burn streaks\n");
+   printf(" psf=fname      Output file for PSF FITS stamp gallery\n");
+   printf(" psfstat=fname  Output file for PSF statistics listing\n");
+   printf(" psfavg=N       List PSF statistics averaged over 2^N cells (N=0:3)\n");
+   printf(" psfsize=size   How big a box to use for PSF extraction?\n");
+   printf(" psfmin=size    How big must a box be to use for PSF extraction?\n");
+   printf(" psfmaxn=N      Max number of PSF stars per cell\n");
+   printf(" psfctr=N       Max distance for max centering\n");
+   printf(" psf3dfits={t|f} Write 3D FITS for PSF instead of Concat (default)?\n");
+   printf(" thrburn=X      Threshold for onset of burning\n");
+   printf(" thrtrail=X     Trailing might go this low\n");
+   printf(" thrmax=X       Possibly trailing stars?\n");
+   printf(" thrstar=X      Threshold for star above sky\n");
+   printf(" fracstar=X     Fraction of peak to follow star profile\n");
+   printf(" thrpsf=X       Threshold for a star to be a PSF\n");
+   printf(" rmask=X        Diameter growth factor of burned spots\n");
+   printf(" bmask=X        Box size growth of burn/star boxes\n");
+   printf(" quiet={t|f}    Quiet?\n");
+   printf(" verbose=N      Set verbosity bits:\n");
+   printf("    0x0001         Normal, verbose output\n");
+   printf("    0x0002         Dump detection process\n");
+   printf("    0x0004         Dump out PSF selection process\n");
+   printf("    0x0008         Dump fit progress\n");
+   printf("    0x0010         Dump fit profiles\n");
+   printf("    0x0020         Write Vista marker procedure to /tmp/markem.pro\n");
+   printf("    0x0040         Dump box growth diagnostics\n");
+   printf("    0x0080         Write mask in place of corrected image\n");
+}
Index: /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/burntool/burntool.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/burntool/burntool.h	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/burntool/burntool.h	(revision 23594)
@@ -0,0 +1,213 @@
+/* Burn correction routines:
+   -------------------------  
+ *  1. Identify burned spots from saturation threshold
+ ?  2. Identify burns and trails morphologically?
+ ?    2.1 Look for flat-topped stars
+ ?    2.2 Look for asymmetric, smooth, declining trails?
+ ?    2.3 Use y overscan?
+ ?    2.4 Bin in x?
+ *  3. Mask stars and other cruft?
+ *  4. Fit up and down row functions
+ *  5. Read burn time list
+ *  6. Write updated burn time list
+ *  8. Undo subtraction
+    7. Create and save burn correction FITS table
+*/
+/* Algorithm:
+   ----------
+    1. Find new burned spots
+    2. Create new burn entries: x1,x2,y,time=now
+    3. For each burn:
+        if t = now  detrail up
+              else  deburn down
+        if fit negligible  delete burn 
+                     else  save burn fits
+ */
+/* ToDo:
+   -----
+ *  clean up fit start points, esp for persistence
+ *  change burn= and persist= to trailin= and trailout=
+ *  trim edges of fits
+ *  implement MASK_SAT
+ >  test for garbage fits (positive slope, etc)
+ *  test nearly saturated stars for burn.
+ *  undo subtraction (almost perfect -- occasional pixels and baddies)
+ *  star gather into 3D FITS
+ *  test on donuts
+ *  stamp extraction center on box, not max
+ *  Add back nominal bias; output as ushort   
+ *  man page... ongoing...
+ *  Scale uses peak/sum?
+ *  Change psf coords to center; save in header not pixels
+ x  better trail function?  A tuned ln(x+A) is better, but too hard.
+ *  why various restorations imperfect?  edge of cell?
+ *  cell mask argument
+... why the honker star not ID'ed as a burn?  various other gotchas...
+ *  age away old persistence streaks; merge persistence streaks
+ *  PSF info on median psf
+ *  clean up sky/rms calculation with 5*quartile clipping
+ *  change VERBOSE to a bitmask for access to various portions of the code
+ *  add access to a lot of the tuning variables...
+ *  update man page
+    tuning, tuning, tuning...
+    add correction FITS table to MEF
+*/
+
+#define MAX(a,b) (((a) > (b)) ? (a) : (b))
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+#define NINT(x) (x<0?(int)((x)-0.5):(int)((x)+0.5))
+#define ABS(a) (((a) > 0) ? (a) : -(a))
+
+// #define STATIC static	/* To make function declarations static */
+#define STATIC			/* To make function declarations global */
+
+#define MAXCELL	64		/* Max cells in an OTA */
+#define MAXSIZE 700		/* Maximum vertical cell size */
+
+#define STAR_RADIUS  4		/* Radius over which a star ctr must be max */
+#define SKY_MARG 3		/* Horiz offset from burn area for sky */
+#define FIT_EDGE 10		/* How far beyond saturation to start fit? */
+#define Y_SCALE 0.01		/* Scale factor for y in fits */
+
+typedef signed short int IMTYPE;/* Data type of image */
+
+typedef int DTYPE;		/* Data type of data copy */
+#define NODATA 0		/* Marker for *No Data */
+
+/* Mask codes: keep in order of severity for psf_select()! */
+typedef int MTYPE;		/* Data type of mask */
+#define MASK_NONE      0	/* Unmasked pixel (MUST be 0) */
+#define MASK_STAR_HALO 1	/* Extended halo box from star */
+#define MASK_SAT_HALO  2	/* Extended halo box from grow_mask() */
+#define MASK_CTR       3	/* Center mask from star_detect() */
+#define MASK_SAT       4	/* Center mask of saturated pixels */
+
+/* Enumeration of function choices */
+#define FUNC_NONE 0		/* No associated function */
+#define BURN_PWR  1		/* Power law */
+#define BURN_EXP  2		/* Exponential */
+#define PSF_STAR  3		/* Unfitted: good psf star */
+
+/* Fit error codes */
+#define FIT_ERROR  1		/* linearfit failed */
+#define FIT_TOP_ERROR  2	/* Saturation extends to top: no points */
+#define FIT_SLOPE_ERROR  3	/* Unreasonable fit */
+
+/* Fit parameters */
+#define FIT_MIN_SLOPE  -5.0	/* minimum slope which is a credible fit */
+#define FIT_MAX_SLOPE   0.0	/* maximum slope which is a credible fit */
+
+/* Verbosity bits */
+#define VISTAMARKER "/tmp/markem.pro"	/* Write Vista pro to mark stars?*/
+#define VERB_NORM     0x0001	/* Normal, verbose output */
+#define VERB_DETECT   0x0002	/* Dump detection process */
+#define VERB_PSFSEL   0x0004	/* Dump out PSF selection process */
+#define VERB_FIT      0x0008	/* Dump fit progress */
+#define VERB_FITPROF  0x0010	/* Dump fit profiles */
+#define VERB_VISTA    0x0020	/* Write vista markers as /tmp/markem.pro */
+#define VERB_BOXGROW  0x0040	/* Dump box growth diagnostics */
+#define VERB_MASK     0x0080	/* Write mask in place of corrected image */
+
+/* Description of a potentially burned trail */
+typedef struct obj_box {
+      int cell;		/* what cell is this one in? */
+      int time;		/* PON time when it was created */
+      int sx;		/* left corner */
+      int sy;		/* bottom corner */
+      int ex;		/* right corner */
+      int ey;		/* top corner */
+      int cx;		/* center x (position of max) */
+      int cy;		/* center y (position of max) */
+      int max;		/* max data value (above sky) */
+      int y0m;		/* min y value at sx */
+      int y0p;		/* max y value at sx */
+      int y1m;		/* min y value at ex */
+      int y1p;		/* max y value at ex */
+      int x0m;		/* min x value at sy */
+      int x0p;		/* max x value at sy */
+      int x1m;		/* min x value at ey */
+      int x1p;		/* max x value at ey */
+      int sat;		/* saturated (i.e. bigger than BURNTHRESH)? */
+      int func;		/* what are we going to do about it? */
+      int diff;		/* median diff of up minus down (sum for stars) */
+      int up;		/* does it trail up or down? (stamp sx for stars) */
+      int y0;		/* y origin for the fit (stamp sy for stars) */
+      int burned;	/* do we think it's left a burn trail? */
+      int fiterr;	/* error fitting the trail? */
+      int sxfit;	/* starting column for fits */
+      int exfit;	/* ending column for fits */
+      int nfit;		/* how many columns were corrected? */
+      IMTYPE *stamp;	/* postage stamp of this object */
+      int *xfit;	/* x of each value of the start of correction */
+      int *yfit;	/* y value of the start of correction */
+      double slope;	/* slope of fit (stamp sum/max) */
+      double *zero;	/* zero of fit for each of the columns */
+} OBJBOX;
+
+/* Info for an entire cell */
+typedef struct cell_info {
+      int cell;			/* Cell number */
+      int bias;			/* Bias level */
+      int sky;			/* Sky level */
+      int rms;			/* RMS in the sky */
+      int time;			/* PON time of this cell */
+      int nburn;		/* Number of trails left by sat stars */
+      OBJBOX *burn;		/* Stars which we think left a trail */
+      int npersist;		/* Number of old persistence streaks */
+      OBJBOX *persist;		/* Persistent streaks */
+      int nstar;		/* Number of stars */
+      OBJBOX *star;		/* Stars, harmless we think */
+} CELL;
+
+/* Prototypes */
+STATIC int mem_init(int nx, int ny, int NX, int NY);
+STATIC int burn_fix(int nx, int ny, int stride, int NY, IMTYPE *buf, 
+		    CELL *cell, int cellnum);
+STATIC int burn_test(int nx, int ny, int NX, DTYPE *data, int rms,
+		     MTYPE *mask, OBJBOX *box);
+STATIC int burn_restore(int nx, int ny, int NX, IMTYPE *buf, CELL *cell);
+STATIC int persist_read(CELL *cell, const char *infile);
+STATIC int persist_write(CELL *cell, const char *outfile);
+STATIC int persist_fix(int nx, int ny, int stride, IMTYPE *buf, CELL *cell);
+STATIC int persist_merge(CELL *cell);
+
+STATIC int star_detect(int nx, int ny, int NX, int NY, DTYPE *data,
+		       MTYPE *mask, MTYPE *veto, CELL *cell, int cellnum);
+STATIC int burn_check(int nx, int ny, int stride, int NY, DTYPE *buf,
+		       MTYPE *mask, CELL *cell);
+
+STATIC int cell_stats(int nx, int ny, int NX, int NY, DTYPE *data, CELL *cell);
+STATIC int burn_blab(CELL *cell);
+STATIC int persist_blab(CELL *cell);
+STATIC int vista_marker(CELL *cell, char *fname);
+
+STATIC int fit_trail(int nx, int ny, int NX, DTYPE *data, MTYPE *mask, 
+		    OBJBOX *box, int up, int sky, int rms, int fitfunc);
+STATIC int sub_fit(int nx, int ny, int NX, IMTYPE *buf, OBJBOX *box, int sign);
+
+STATIC int psf_select(int nx, int ny, int NX, MTYPE *mask, DTYPE *buf,
+		      int nbox, OBJBOX *box, int size, int sky);
+STATIC int psf_write(int nx, int ny, CELL *OTA, int otanum, const char *psffile);
+STATIC int psf_write_stats(int nx, int ny, CELL *OTA, int otanum, const char *statfile, int psfavg);
+STATIC int psf_stats(int nx, int ny, IMTYPE *data, int bias, 
+		     double *fwhm, double *q);
+
+STATIC int grow_box(int nx, int ny, int NX, DTYPE *data, int thr, OBJBOX *box);
+STATIC int grow_mask(int nx, int ny, int NX, DTYPE *mask, double xfac, 
+		     double yfac, int maskval, int nbox, OBJBOX *box);
+STATIC int local_max(int i, int j, int r, int nx, int ny, int NX, DTYPE *data);
+
+STATIC int wlinearfit(int npt, double *x, double *y, 
+		      double *w, double *a, double *b);
+STATIC int int_median(int n, int *key);
+STATIC double double_median(int n, double *key);
+
+STATIC int qsort_int(int n, int *key);
+STATIC double qsort_dbl(int n, double *key);
+
+STATIC void syntax(const char *prog);
+
+int write_2dfits(int nx, int ny, int sx, int sy, IMTYPE *data, int fd);
+int write_3dhdr(int nx, int ny, int nz, int ncmt, char *cmt[], int fd);
+int write_2ddata(int nx, int ny, int *ntot, IMTYPE *data, int fd);
+int write_3dend(int *ntot, int fd);
Index: /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/burntool/burnutils.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/burntool/burnutils.c	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/burntool/burnutils.c	(revision 23594)
@@ -0,0 +1,290 @@
+/* burnutils.c - various utility functions */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "math.h"
+
+#include "burntool.h"
+#include "burnparams.h"
+
+/****************************************************************/
+/* Slide out the walls of a box until they fall below thresh; bottom fixed */
+STATIC int grow_box(int nx, int ny, int NX, DTYPE *data, int thr, OBJBOX *box)
+{
+   int i, j, is, ie, left, right;
+
+   left = right = 1;
+
+   if(VERBOSE & VERB_BOXGROW) {
+      printf("box: %d %d %d %d\n", box->sx, box->sy, box->ex, box->ey);
+   }
+
+   do {
+/* Push the right wall until it's clear */
+      if(right) {
+	 for(i=box->ex+1; i<nx; i++) {
+	    for(j=box->sy; j<=box->ey; j++) if(data[i+j*NX] >= thr) break;
+	    if(j > box->ey) break;
+	    if(VERBOSE & VERB_BOXGROW) {
+	       printf("right: %8d %8d\n", i, j);
+	    }
+	 }
+	 box->ex = i - 1;
+	 right = 0;
+      }
+
+/* Push the left wall until it's clear */
+      if(left) {
+	 for(i=box->sx-1; i>=0; i--) {
+	    for(j=box->sy; j<=box->ey; j++) if(data[i+j*NX] >= thr) break;
+	    if(j > box->ey) break;
+	    if(VERBOSE & VERB_BOXGROW) {
+	       printf("left: %8d %8d\n", i, j);
+	    }
+	 }
+	 box->sx = i + 1;
+	 left = 0;
+      }
+
+/* Push the top wall until it's clear, catching corners! */
+      is = MAX(0, box->sx-1);
+      ie = MIN(nx-1, box->ex+1);
+      for(j=box->ey+1; j<ny; j++) {
+	 for(i=is; i<=ie; i++) if(data[i+j*NX] >= thr) break;
+	 if(i == is && box->sx>0) left = 1;	/* Need to redo left */
+	 if(data[ie+j*NX] >= thr && box->ex<nx-1) right = 1;  /* Redo right */
+	 if(i > ie) break;			/* All clear */
+	 if(VERBOSE & VERB_BOXGROW) {
+	    printf("top: %8d %8d\n", i, j);
+	 }
+      }
+      box->ey = j - 1;
+
+/* Push the bottom wall until it's clear, catching corners! */
+      is = MAX(0,box->sx-1);
+      ie = MIN(nx-1,box->ex+1);
+      for(j=box->sy-1; j>=0; j--) {
+	 for(i=is; i<=ie; i++) if(data[i+j*NX] >= thr) break;
+	 if(i == is && box->sx>0) left = 1;	/* Need to redo left */
+	 if(data[is+1+j*NX] >= thr) box->y0m = j;
+	 if(data[ie+j*NX] >= thr && box->ex<nx-1) right = 1;  /* Redo right */
+	 if(data[ie-1+j*NX] >= thr) box->y1m = j;
+	 if(i > ie) break;			/* All clear */
+	 if(VERBOSE & VERB_BOXGROW) {
+	    printf("bottom: %8d %8d %8d %8d\n", i, j, box->y0m, box->y1m);
+	 }
+      }
+      box->sy = j + 1;
+
+      if(left) box->sx -= 1;
+      if(right) box->ex += 1;
+
+   } while(left || right);
+
+/* Find the side saturation points */
+   for(j=box->sy; j<box->ey; j++) if(data[box->sx+j*NX] >= thr) break;
+   box->y0m = j;
+
+   for(j=box->ey; j>box->sy; j--) if(data[box->sx+j*NX] >= thr) break;
+   box->y0p = j;
+
+   for(j=box->sy; j<box->ey; j++) if(data[box->ex+j*NX] >= thr) break;
+   box->y1m = j;
+
+   for(j=box->ey; j>box->sy; j--) if(data[box->ex+j*NX] >= thr) break;
+   box->y1p = j;
+
+   for(i=box->sx; i<box->ex; i++) if(data[i+box->ey*NX] >= thr) break;
+   box->x1m = i;
+
+   for(i=box->ex; i>box->sx; i--) if(data[i+box->ey*NX] >= thr) break;
+   box->x1p = i;
+
+   for(i=box->sx; i<box->ex; i++) if(data[i+box->sy*NX] >= thr) break;
+   box->x0m = i;
+
+   for(i=box->ex; i>box->sx; i--) if(data[i+box->sy*NX] >= thr) break;
+   box->x0p = i;
+
+   if(VERBOSE & VERB_BOXGROW) {
+      printf("box: %d %d %d %d\n", box->sx, box->sy, box->ex, box->ey);
+   }
+
+   return(0);
+}
+
+/****************************************************************/
+/* Expand the mask by pushing out boxes by a factor */
+STATIC int grow_mask(int nx, int ny, int NX, DTYPE *mask, double bfac, 
+		     double rfac, int maskval, int nbox, OBJBOX *box)
+{
+   int i, j, k, x0, y0, db, dr, r2, i0, i1, j0, j1;
+
+   for(k=0; k<nbox; k++) {
+/* Push out the box by a distance bfac */
+      db = NINT(bfac);
+      j0 = MAX(0, box[k].sy-db);
+      j1 = MIN(ny-1, box[k].ey+db);
+      i0 = MAX(0, box[k].sx-db);
+      i1 = MIN(nx-1, box[k].ex+db);
+
+      for(j=j0; j<=j1; j++) {
+	 for(i=i0+1; i<i1; i++) {
+	    if(mask[i+j*NX] == MASK_NONE) mask[i+j*NX] = maskval;
+	    if(mask[i+j*NX] == MASK_NONE) mask[i+j*NX] = maskval;
+	 }
+      }
+
+/* Push out the center to a diameter rfac*xsize */
+      x0 = (box[k].sx + box[k].ex + 1) / 2;
+      y0 = (box[k].y0p + box[k].y1p + box[k].y0m + box[k].y1m + 2) / 4;
+      dr = NINT((box[k].ex - box[k].sx + 1) * 0.5 * rfac);
+      for(j=MAX(0,y0-dr); j<=MIN(ny-1, y0+dr); j++) {
+	 for(i=MAX(0,x0-dr); i<=MIN(nx-1, x0+dr); i++) {
+	    r2 = (i-x0)*(i-x0) + (j-y0)*(j-y0);
+	    if(r2 < dr*dr && mask[i+j*NX] == MASK_NONE)
+	       mask[i+j*NX] = maskval;
+	 }
+      }
+   }
+   return(0);
+}
+
+#if 0
+/****************************************************************/
+/* Expand the mask by pushing out boxes by a factor */
+STATIC int grow_mask(int nx, int ny, int NX, DTYPE *mask, 
+		     double xfac, double yfac, int nbox, OBJBOX *box)
+{
+   int i, j, k, x0, y0, dx, dy;
+
+   for(k=0; k<nbox; k++) {
+      x0 = (box[k].sx + box[k].ex + 1) / 2;
+      y0 = (box[k].sy + box[k].ey + 1) / 2;
+      dx = NINT((box[k].ex - box[k].sx + 1) * 0.5 * xfac);
+      dy = NINT((box[k].ey - box[k].sy + 1) * 0.5 * yfac);
+      for(j=MAX(0,y0-dy); j<MIN(ny, y0+dy); j++) {
+	 for(i=MAX(0,x0-dx); i<MIN(nx, x0+dx); i++) {
+	    if(mask[i+j*NX] == MASK_NONE) mask[i+j*NX] = MASK_HALO;
+	 }
+      }
+   }
+   return(0);
+}
+#endif
+
+/****************************************************************/
+/* Test to see whether a spot is a not-so-local maximum */
+STATIC int local_max(int i, int j, int r, int nx, int ny, int NX, DTYPE *data)
+{
+   int k, l;
+   if(i < r || i > nx-1-r || j < r || j > ny-1-r) return(0);
+   if(data[i+j*NX] <= data[i+1+j*NX] || 
+      data[i+j*NX] <  data[i-1+j*NX] ||
+      data[i+j*NX] <= data[i+(j+1)*NX] ||
+      data[i+j*NX] <  data[i+(j-1)*NX]) return(0);
+   for(l=-r; l<=r; l++) {
+      for(k=-r; k<=r; k++) {
+	 if(data[i+j*NX] < data[i+k+(j+l)*NX]) return(0);
+      }
+   }
+   return(1);
+}
+
+#ifndef INSERT_MEDIAN	/* Faster quick sort */
+/****************************************************************/
+STATIC int int_median(int n, int *key)
+{
+   if(n == 0) return(0);
+   qsort_int(n, key);
+   return((key[n/2]+key[(n-1)/2]) / 2);
+}
+
+/****************************************************************/
+STATIC double double_median(int n, double *key)
+{
+   if(n == 0) return(0.0);
+   qsort_dbl(n, key);
+   return( 0.5*(key[n/2]+key[(n-1)/2]) );
+}
+
+#else	/* Slower sort by insertion */
+/****************************************************************/
+STATIC int int_median(int n, int *key)
+{
+   int i, j, k;
+   int tmp;
+
+   if(n == 0) return(0);
+   for(j=n-2; j>=0; j--) {
+      for(i=j+1, k=j; i<n; i++) {
+	 if(key[j] <= key[i]) break;
+	 k = i;
+      }
+      if(k == j) continue;
+      tmp = key[j];
+      for(i=j+1; i<=k; i++) {
+	 key[i-1] = key[i];
+      }
+      key[k] = tmp;
+   }
+   i = (key[n/2]+key[(n-1)/2]) / 2;
+   return(i);
+}
+
+/****************************************************************/
+STATIC double double_median(int n, double *key)
+{
+   int i, j, k;
+   double tmp;
+
+   if(n == 0) return(0.0);
+   for(j=n-2; j>=0; j--) {
+      for(i=j+1, k=j; i<n; i++) {
+	 if(key[j] <= key[i]) break;
+	 k = i;
+      }
+      if(k == j) continue;
+      tmp = key[j];
+      for(i=j+1; i<=k; i++) {
+	 key[i-1] = key[i];
+      }
+      key[k] = tmp;
+   }
+   return( 0.5*(key[n/2]+key[(n-1)/2]) );
+}
+#endif
+
+/****************************************************************/
+/* Fit y = ax + b */
+STATIC int wlinearfit(int npt, double *x, double *y, double *w, 
+		      double *a, double *b)
+{
+   int i;
+   double v0=0.0, v1=0.0, m00=0.0, m01=0.0, m11=0.0, det;
+
+/* Accumulate sums for least squares fit */
+   for(i=0; i<npt; i++) {
+      v0 += y[i] * w[i];
+      v1 += y[i] * x[i] * w[i];
+      m00 += w[i];
+      m01 += x[i] * w[i];
+      m11 += x[i] * x[i] * w[i];
+   }
+
+/* And solve the matrix */
+   det = m00 * m11 - m01 * m01;
+   if(det == 0.0) {
+      *a = *b = 0.0;
+      return(-1);
+   }
+   *b = (v0*m11-v1*m01) / det;
+   *a = (v1*m00-v0*m01) / det;
+   return(0);
+}
Index: /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/burntool/cheapfits.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/burntool/cheapfits.c	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/burntool/cheapfits.c	(revision 23594)
@@ -0,0 +1,93 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+
+#define BZERO 32768
+#define NFITS 2880
+typedef signed short int IMTYPE;	/* Data type of image */
+
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+
+char fitsbuf[NFITS];
+static int _ENDIAN_TEST = 1;
+#define LOWENDIAN() (*(char*)&_ENDIAN_TEST)
+
+void swab(const void *from, void *to, ssize_t n);
+
+/****************************************************************/
+/* write_2dfits() writes a single 2D FITS file */
+int write_2dfits(int nx, int ny, int sx, int sy, IMTYPE *data, int fd)
+{
+   int i, n;
+   for(n=0; n<NFITS; n++) fitsbuf[n] = ' ';
+   n = 0;
+   sprintf(fitsbuf+80*n++, "%-8s= %20s", "SIMPLE", "T");
+   sprintf(fitsbuf+80*n++, "%-8s= %20d", "BITPIX", 16);
+   sprintf(fitsbuf+80*n++, "%-8s= %20d", "NAXIS", 2);
+   sprintf(fitsbuf+80*n++, "%-8s= %20d", "NAXIS1", nx);
+   sprintf(fitsbuf+80*n++, "%-8s= %20d", "NAXIS2", ny);
+   sprintf(fitsbuf+80*n++, "%-8s= %20d", "CNPIX1", sx);
+   sprintf(fitsbuf+80*n++, "%-8s= %20d", "CNPIX2", sy);
+   sprintf(fitsbuf+80*n++, "%-8s= %20.1f", "BSCALE", 1.0);
+   sprintf(fitsbuf+80*n++, "%-8s= %20.1f", "BZERO", (double)BZERO);
+   sprintf(fitsbuf+80*n++, "%-8s",       "END");
+   for(n=0; n<NFITS; n++) if(fitsbuf[n]=='\0') fitsbuf[n] = ' ';
+   if(write(fd, fitsbuf, NFITS) < 0) return(-1);
+   for(i=0; i<nx*ny; i+=NFITS/sizeof(IMTYPE)) {
+      n = MIN(NFITS, (nx*ny-i) * sizeof(IMTYPE));
+      if(LOWENDIAN()) swab(data+i, fitsbuf, n);
+      else            memcpy(fitsbuf, data+i, n);
+      if(n < NFITS) bzero(fitsbuf+n, NFITS-n);
+      if(write(fd, fitsbuf, NFITS) < 0) return(-1);
+   }
+   return(0);
+}
+
+/****************************************************************/
+/* write_3dhdr() writes a bare 3D FITS header */
+int write_3dhdr(int nx, int ny, int nz, int ncmt, char *cmt[], int fd)
+{
+   int i, n;
+   for(n=0; n<NFITS; n++) fitsbuf[n] = ' ';
+   n = 0;
+   sprintf(fitsbuf+80*n++, "%-8s= %20s", "SIMPLE", "T");
+   sprintf(fitsbuf+80*n++, "%-8s= %20d", "BITPIX", 16);
+   sprintf(fitsbuf+80*n++, "%-8s= %20d", "NAXIS", 3);
+   sprintf(fitsbuf+80*n++, "%-8s= %20d", "NAXIS1", nx);
+   sprintf(fitsbuf+80*n++, "%-8s= %20d", "NAXIS2", ny);
+   sprintf(fitsbuf+80*n++, "%-8s= %20d", "NAXIS3", nz);
+   sprintf(fitsbuf+80*n++, "%-8s= %20.1f", "BSCALE", 1.0);
+   sprintf(fitsbuf+80*n++, "%-8s= %20.1f", "BZERO", (double)BZERO);
+   for(i=0; i<ncmt; i++) sprintf(fitsbuf+80*n++, "COMMENT %s", cmt[i]);
+   sprintf(fitsbuf+80*n++, "%-8s",       "END");
+   for(n=0; n<NFITS; n++) if(fitsbuf[n]=='\0') fitsbuf[n] = ' ';
+   if(write(fd, fitsbuf, NFITS) < 0) return(-1);
+   return(0);
+}
+
+/****************************************************************/
+/* write_2ddata() writes a single data chunk as part of a 3D fits file */
+int write_2ddata(int nx, int ny, int *ntot, IMTYPE *data, int fd)
+{
+   int i, n=0;
+   for(i=0; i<nx*ny; i+=NFITS/sizeof(IMTYPE)) {
+      n = MIN(NFITS, (nx*ny-i) * sizeof(IMTYPE));
+      if(LOWENDIAN()) swab(data+i, fitsbuf, n);
+      else            memcpy(fitsbuf, data+i, n);
+      if(write(fd, fitsbuf, n) < 0) return(-1);
+   }
+   *ntot = *ntot + n;
+   return(0);
+}
+
+/****************************************************************/
+/* write_3dend() writes a single data chunk as part of a 3D fits file */
+int write_3dend(int *ntot, int fd)
+{
+   if((*ntot%NFITS) != 0) {
+      bzero(fitsbuf, NFITS-(*ntot%NFITS));
+      if(write(fd, fitsbuf, NFITS-(*ntot%NFITS)) < 0) return(-1);
+   }
+
+   return(0);
+}
Index: /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/burntool/fitstable.spec
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/burntool/fitstable.spec	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/burntool/fitstable.spec	(revision 23594)
@@ -0,0 +1,64 @@
+We need two FITS tables to describe the burn corrections.  Each
+correction area is basically a rectangle within a cell and there are a
+bunch of parameters which describe the size and other properties of
+the rectangle.
+
+Then, for each column in each rectangle there are three parameters
+which describe the burn fit: the start x,y and one fit parameter (the
+amplitude; all fits share the same shape).  The rows in this table
+need to have a pointer back to the first table of generic rectangle info.
+
+There is also data calculated for each cell: bias, sky, and
+background, which are probably not worth keeping.
+
+The persist_read() routine reads the tabulated quantities and stuffs
+them into an array of OBJBOX structures, and these are then copied to
+an array of CELL structures, one for each of the 64 cells, using the
+OBJBOX pointer and count.  The persist_write() routine writes this
+information to a text file.  (The structures are in burntool.h; the
+read/write routines are in persistio.c.)
+
+Here are the entries for the first table, listed as entries in an
+OBJBOX structure:
+
+  int cell;      /* what cell is this one in? */
+  int time;      /* PON time when it was created */
+  int cx;        /* center x (position of max) */
+  int cy;        /* center y (position of max) */
+  int max;       /* max data value (above sky) */
+  int y0;        /* y origin for the fit (stamp sy for stars) */
+  int sx;        /* left corner */
+  int sy;        /* bottom corner */
+  int ex;        /* right corner */
+  int ey;        /* top corner */
+  int y0m;       /* min y value at sx */
+  int y0p;       /* max y value at sx */
+  int y1m;       /* min y value at ex */
+  int y1p;       /* max y value at ex */
+  int x0m;       /* min x value at sy */
+  int x0p;       /* max x value at sy */
+  int x1m;       /* min x value at ey */
+  int x1p;       /* max x value at ey */
+  int func;      /* what are we going to do about it? */
+  int up;        /* does it trail up or down? */
+  int nfit;      /* how many columns were corrected? */
+  int sxfit;     /* starting column for fits */
+  int exfit;     /* ending column for fits */
+  double slope;  /* slope of fit */
+
+Here are the entries in the second table, listed as entries in three
+arrays which are part of the OBJBOX structure.  In addition each entry
+in the second pointer requires a pointer back to its burn rectangle in
+the first table, which can be an integer coding the (cell,cx,cy)
+parameters because they will be unique.
+
+  int objptr;    /* Cell,cx,cy coded as ccxxxyyy */
+  int xfit;	 /* x of each value of the start of correction */
+  int yfit;	 /* y value of the start of correction */
+  double zero;	 /* zero of fit for each of the columns */
+
+The CELL structure only needs to have the following two entries filled
+in for each of the 64 structures in the array describing the entire OTA:
+
+  int npersist;	    /* Number of old persistence streaks */
+  OBJBOX *persist;  /* Persistent streaks */
Index: /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/burntool/man/burntool.1
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/burntool/man/burntool.1	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/burntool/man/burntool.1	(revision 23594)
@@ -0,0 +1,250 @@
+.nf
+NAME:
+	burntool - repair saturation trails and burns; extract PSF stamps
+
+SYNOPSIS:
+	burntool MEF_image [options]
+
+DESCRIPTION: 
+	Burntool reads an OTA's MEF, cell by cell, looking for and correcting
+	trails left above saturated stars and persistence burns below stars
+	from previous images.  It rewrites the MEF in place.  In addition,
+	burntool identifies bright stars which are not saturated and can write
+	a concatenated or 3D FITS image of postage stamps and a median of 
+	the scaled star images.  Burntool also writes a table of the fits to
+	trails and persistence burns which can serve as input for the next
+	image to be corrected.
+	
+	Because the profile of saturated stars can be complex, burntool
+	identifies objects by noting when the images passes a threshold
+	(BURN_THRESH, default 30000 ADU) and then pushing out a bounding box
+	which encompasses all adjoining pixels which exceed a lower
+	TRAIL_THRESH (default 10000 ADU).  The pixels comprising the octagon
+	exceeding TRAIL_THRESH on this bounding box are noted, as is the pixel
+	and value of the maximum.
+	
+	In addition, burntool performs the same action on pixels which pass a
+	(much lower) STAR_THRESH above sky (default 1000 ADU) and which are
+	also the local maximum over all pixels within STAR_RADIUS (default 4)
+	distance.  These pixels are boxed down to a fraction STAR_FRAC
+	(default 0.3) of this maximum above sky.
+	
+	Once all objects are boxed, all saturated and non-saturated stars
+	whose maximum exceeds MAX_THRESH (default 20000 ADU) are tested for
+	trailing by comparing the flux beyond the bounding box above and below
+	the star.  Those whose median difference exceeds 0.3 times the rms of
+	the sky are designated as "trailed" and are nominated for a trail fit.
+	
+	The list of boxes is used to create a mask and it is expanded by a
+	factor of RMASK_GROW (default 3.0) in diameter and BMASK_GROW (default
+	5) pixels in the bounding box "burns" and "stars".  This mask is used
+	to veto pixels from participating in fits and is also used to select
+	isolated objects for postage stamps.
+	
+	Burntool also can read an input file which lists burns which may exist
+	because of readout of saturated stares from previous exposures.  This
+	forms the third category of boxes that burntool processes: "burns".
+	
+	Burntool proceeds to fit and remove each trail and burn, by forming a
+	median across the box's width in x to create an average profile in y,
+	fitting this profile either as an exponential or a power law,
+	refitting in each column for an individual scale factor and starting
+	point, and then subtracting the fit.  Upward trails are fitted as
+	power laws; downward burns as exponentials.
+	
+	Burntool finishes by rewriting the modified MEF and all of the fits in
+	an output file ("out=file_name").  If desired, burntool can be run
+	with "restore=t" and this output table as input ("in=file_name") to
+	restore the MEF to its pre-subtraction state.  Burntool can also be
+	inhibited from subtracting the fits by "update=f".
+	
+	The identification of significant (but unsaturated) stars is an
+	important component of burntool's mask generation, and if requested
+	burntool can assemble a gallery of postage stamps of suitable stars.
+	
+	Any star whose maximum exceeds PSF_THRESH above sky but is not deemed
+	to be saturated or trailed can contribute if its box does not overlap
+	the mask (other than its own contribution) or impinge on the edge of
+	the cell.  If the maximum pixel is less than PSF_CTR_TOL (default 8)
+	pixels from the center of its box, the stamp is centered on that
+	pixel; otherwise the stamp is centered on the bounding box. If the box
+	is smaller than MIN_PSF_SIZE (default 3) the star is not used for a
+	stamp.  The maximum number of stars which can be so designated as
+	PSF stars from a single cell is MAX_PSF_PER_CELL (default 20).  The
+	selection proceeds from bottom to top, so the lowest 20 are used.
+	(Note that it is extremely uncommon for any 2.5 arcmin cell to have
+	as many as 20 stars bright enough for PSF consideration.)
+	
+	This gallery is written as a concatenated or 3D FITS file whose name
+	is designated by "psf=file_name" and the size of the stamps is
+	governed by "psfsize=N" (default 32).  The first entry in the 3D FITS
+	file is the median of all the individual stamps, scaled to bring the
+	peak to approximately 10000 ADU.  The stamps all have sky subtracted
+	and an artificial bias of USHORT_BIAS (default 1000) added back in.
+
+	Note that the first two pixels in the individual stamps might be the 
+	OTA coordinates of the origin of the stamp, not flux data.
+
+	If PSF stamp output is requested, burntool will measure statistics
+	of the median PSF image and report a single line on stdout.  The
+	entries in this line are (for example):
+
+	   N= 199	   Number of PSF stars (median means N+1 are written)
+	   PSFmaj= 6.93	   Major axis FWHM [pix] (0.0 if fit fails)
+	   min= 3.96	   Minor axis FWHM [pix] (0.0 if fit fails)
+	   theta= 5.3	   Angle of FWHM from x [deg] (0.0 if fit fails)
+	   m2= 12.11	   Second moment of distribution [pix^2]
+	   q+= 5.577	   Plus quadrupole qxx-qyy [pix^2]
+	   qx= 0.710	   Cross quadrupole 2*qxy [pix^2]
+	   qt= -5.562      Tangential quadrupole [pix^2] 
+				q+ * cos(2*phi) + qx * sin(2*phi)
+
+	The argument "psfstat=file_name" requests burntool to assemble the
+	PSF stamps and calculate statistics on them on a cell-by-cell basis.
+	While noisier than the median PSF for the entire OTA, it provides
+	intra-OTA resolution of PSF changes.  This entries in this file
+	are (for example):
+
+	   ext=xy13        Cell xy ID
+	   bias=7940       Cell bias level [ADU]
+	   sky=206         Cell sky level [ADU]
+	   rmssky=15       Cell sky RMS [ADU]
+	   npsf=3          Number of contributing PSF stars
+	   fwhm=5.38       PSF FWHM [pix] (0.0 if fit fails)
+	   m2=11.08        Second moment of distribution [pix^2] 
+	   qp=5.154        Plus quadrupole qxx-qyy [pix^2]	 
+	   qc=0.560        Cross quadrupole 2*qxy [pix^2]	 
+	   qt=-5.201       Tangential quadrupole [pix^2] 	  
+			   	q+ * cos(2*phi) + qx * sin(2*phi)
+
+	Helpful utilities include:
+
+	  Make a mosaic out of the cells of the MEF
+
+	    fitstile \!o4803g0054o30.mos +0-0:o4803g0054o30.fits
+	    fitstile \!o4881g0049o44.mos -0+0:o4881g0049o44.fits
+
+	  Make a mosaic of the 3D FITS postage stamp file
+
+	    optic_fitstile test54.psf test54.tile
+
+OPTIONS:
+	xy=xy          
+		Work on just one cell?  xy ID mode.
+
+	cell=N         
+		Work on just one cell?  Cell count [0:63] mode.
+
+	mask=0101...
+		64 digits to designate which cells [0:63] to work on.
+
+	update={t|f}   
+		Modify the input MEF by subtracting fits (default t)?
+
+	restore={t|f}  
+		Restore the input MEF by adding input fits (default f)?
+
+	in=fname       
+		Input file for previous burn persistence streaks
+
+	out=fname      
+		Output file for burn streaks
+
+	psf=fname      
+		Output file for PSF gallery of postage stamps
+
+	psfsize=size   
+		How big a box to use for PSF extraction?
+
+	psfmin=size   
+		How big must a box be to use for PSF extraction? (PSF_MIN_SIZE)
+
+	psfctr=N
+		Max distance of peak from box center for peak centering
+		(PSF_CTR_TOL)
+
+	psfmaxn=N   
+		Maximum number of PSF stars allowed per cell (MAX_PSF_PER_CELL)
+
+	psf3dfits={t|f}
+		Write 3D FITS for PSF instead of (default) concatenated FITS?
+
+	psfstat=fname      
+		Output file for PSF statistics listing
+
+	psfavg=N
+		List PSF statistics averaged over 2^N cells (N=0:3)
+
+	thrburn=X      
+		Threshold for onset of burning (BURN_THRESH)
+
+	thrtrail=X     
+		Trailing might result from fluxes this low (TRAIL_THRESH)
+
+	thrmax=X       
+		Stars at this peak level might trail (MAX_THRESH)
+
+	thrstar=X      
+		Threshold above sky for star designation (STAR_THRESH)
+
+	fracstar=X     
+		Fraction of peak to follow star profile (STAR_FRAC)
+
+	thrpsf=X       
+		Threshold above sky for a star to be a PSF (PSF_THRESH)
+
+	rmask=X        
+		Diameter growth factor of burned spots (RMASK_GROW)
+
+	bmask=X        
+		Box size growth of burn/star (BMASK_GROW)
+
+	quiet={t|f}    
+		Quiet?
+
+	verbose=N
+		Set verbosity level bitmask:
+		   0x0001   Normal, verbose output
+		   0x0002   Dump detection process
+		   0x0004   Dump out PSF selection process
+		   0x0008   Dump fit progress
+		   0x0010   Dump fit profiles
+		   0x0020   Write Vista marker procedure to /tmp/markem.pro
+		   0x0040   Dump box growth diagnostics
+		   0x0080   Write mask in place of corrected image
+
+
+EXAMPLES:
+	Normal usage:
+
+		burntool o4803g0054o30.fits out=burn.54
+		burntool o4803g0055o30.fits in=burn.54 out=burn.55
+		burntool o4803g0056o30.fits in=burn.55 out=burn.56
+
+	Normal usage and write a PSF file:
+
+		burntool o4803g0054o30.fits out=burn.54 psf=o4803g0054o30.psf
+
+	Just generate a PSF file but don't change the original:
+
+		burntool o4803g0054o30.fits update=f psf=o4803g0054o30.psf
+
+	Restore the subtracted fits:
+
+		burntool o4803g0055o30.fits in=burn.55 restore=t
+
+	Process an out of focus image to get donut stamps:
+
+		burntool o4881g0049q44.fits update=f thrpsf=1000 rmask=0 \
+		psfsize=128 psf=o4881g0049q44_psf.fits
+
+BUGS:
+	090224: Still in development
+
+SEE ALSO:
+	biastool
+
+AUTHORS:
+	John Tonry
+
+VERSION:
Index: /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/burntool/persistfix.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/burntool/persistfix.c	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/burntool/persistfix.c	(revision 23594)
@@ -0,0 +1,65 @@
+/* persistfix.c - fix up persistence streaks */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "math.h"
+
+#include "burntool.h"
+#include "burnparams.h"
+
+/****************************************************************/
+/* persist_fix(): Repair the persistence trails in a cell */
+/* burn_fix must have been run first! */
+STATIC int persist_fix(int nx, int ny, int NX, IMTYPE *buf, 
+		    CELL *cell)
+{
+   int k, err=0;
+
+/* Merge all overlapping streaks */
+   persist_merge(cell);
+
+/* Fix up all the persistence streaks */
+   for(k=0; k<cell->npersist; k++) {
+
+/* Fit the trail */
+      err = fit_trail(nx, ny, NX, imbuf, mbuf, cell->persist+k, 0, 
+	     cell->sky+cell->bias, cell->rms, BURN_EXP);
+
+/* Subtract out the fit */
+      if(!cell->persist[k].fiterr) 
+	 err = sub_fit(nx, ny, NX, buf, cell->persist+k, 1);
+   }
+
+   return(err);
+}
+
+/****************************************************************/
+/* persist_blab(): Tell us about the persistence trails in a cell */
+STATIC int persist_blab(CELL *cell)
+{
+   int i, k, ymid;
+
+/* The persistent streaks */
+   printf("Persistence streaks:\n");
+   printf("  #    cx  cy  max     sx  sy    ex  ey  midy  F   slope  zero\n");
+
+   for(k=0; k<cell->npersist; k++) {
+      i = (cell->persist[k].ex - cell->persist[k].sx + 1) / 2;
+      ymid = (cell->persist[k].y0m + cell->persist[k].y1m +
+	      cell->persist[k].y0p + cell->persist[k].y1p + 2) / 4;
+      printf("%3d %5d %3d %5d %5d %3d %5d %3d %5d %2d %8.5f %4d\n", 
+	     k, cell->persist[k].cx, cell->persist[k].cy,
+	     cell->persist[k].max, 
+	     cell->persist[k].sx, cell->persist[k].sy, 
+	     cell->persist[k].ex, cell->persist[k].ey, ymid,
+	     cell->persist[k].func, 
+	     cell->persist[k].slope, NINT((cell->persist[k]).zero[i]));
+   }
+   return(0);
+}
Index: /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/burntool/persistio.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/burntool/persistio.c	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/burntool/persistio.c	(revision 23594)
@@ -0,0 +1,291 @@
+/* persistio.c - read and write persistence info */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "math.h"
+
+#include "burntool.h"
+#include "burnparams.h"
+
+/****************************************************************/
+/* persist_read(): Read all the persistence trails from a file */
+STATIC int persist_read(CELL *cell, const char *infile)
+{
+   int i, k, nbox=0;
+   char line[1024];
+   FILE *fp;
+
+/* Initialize the counts */
+   for(k=0; k<MAXCELL; k++) {
+      cell[k].npersist = 0;
+      cell[k].persist = NULL;
+   }
+
+   if(infile == NULL) return(0);
+
+   if( (fp=fopen(infile, "r")) == NULL) {
+      fprintf(stderr, "\rerror: cannot open '%s' for reading\n", infile);
+      return(-1);
+   }
+/* Read in all the burns */
+   nbox = 0;
+   while(fgets(line, 1024, fp) != NULL) {
+      if(line[0] == '#') continue;
+      sscanf(line, "%d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %lf %d %d %d",
+	     &boxbuf[nbox].cell, &boxbuf[nbox].time,
+	     &boxbuf[nbox].cx, &boxbuf[nbox].cy,
+	     &boxbuf[nbox].max, &boxbuf[nbox].y0,
+	     &boxbuf[nbox].sx, &boxbuf[nbox].sy, 
+	     &boxbuf[nbox].ex, &boxbuf[nbox].ey,
+	     &boxbuf[nbox].y0m, &boxbuf[nbox].y0p,
+	     &boxbuf[nbox].y1m, &boxbuf[nbox].y1p,
+	     &boxbuf[nbox].x0m, &boxbuf[nbox].x0p,
+	     &boxbuf[nbox].x1m, &boxbuf[nbox].x1p,
+	     &boxbuf[nbox].func, &boxbuf[nbox].up,
+	     &boxbuf[nbox].slope, &boxbuf[nbox].nfit,
+	     &boxbuf[nbox].sxfit, &boxbuf[nbox].exfit);
+      if(boxbuf[nbox].nfit <= 0) continue;
+      boxbuf[nbox].zero = (double *)calloc(boxbuf[nbox].nfit, sizeof(double));
+      boxbuf[nbox].xfit = (int *)calloc(boxbuf[nbox].nfit, sizeof(int));
+      boxbuf[nbox].yfit = (int *)calloc(boxbuf[nbox].nfit, sizeof(int));
+      for(i=0; i<boxbuf[nbox].nfit; i++) {
+	 if(fgets(line, 1024, fp) == NULL) {
+	    fprintf(stderr, "\rerror: short read of burn lines\n");
+	    return(-1);
+	 }
+	 sscanf(line, "%d %d %lf\n", &boxbuf[nbox].xfit[i], 
+		&boxbuf[nbox].yfit[i], &boxbuf[nbox].zero[i]);
+      }
+      boxbuf[nbox].fiterr = 0;
+/* Augment counts */
+      k = boxbuf[nbox].cell;
+      if(k < 0 || k >= MAXCELL) {
+	 fprintf(stderr, "\rerror: illegal cell %d at %d from '%s'\n", 
+		 k, nbox, line);
+	 fclose(fp);
+	 return(-1);
+      }
+      cell[k].npersist += 1;
+
+      if(++nbox >= MAXBURN) {
+	 fprintf(stderr, "\rerror: persist_read overflowed MAXBURN\n");
+	 fclose(fp);
+	 return(-1);
+      }
+   }
+
+/* Allocate some space for them */
+   for(k=0; k<MAXCELL; k++) {
+      if( (i=cell[k].npersist) > 0) {
+	 cell[k].persist = (OBJBOX *)calloc(i, sizeof(OBJBOX));
+	 cell[k].npersist = 0;
+      }
+   }
+/* Copy the results to the cells */
+   for(i=0; i<nbox; i++) {
+      k = boxbuf[i].cell;
+      memcpy(cell[k].persist+cell[k].npersist, boxbuf+i, sizeof(OBJBOX));
+      cell[k].npersist += 1;
+   }
+
+   fclose(fp);
+   return(0);
+}
+
+
+#define DIFFERENT_STREAK 20	/* Proximity for union versus intersection */
+
+/****************************************************************/
+/* persist_merge(): Disentangle overlapping persistence streaks */
+STATIC int persist_merge(CELL *cell)
+{
+   int i, j, k, n, nbox, xs, xe;
+   int jp, kp;
+   int lapmax;
+   OBJBOX *box;
+   int xusage[MAXSIZE], yctr[MAXSIZE], boxid[MAXSIZE];
+   double zk, zj;
+
+/* Look at all the boxes -- if not isolated then merge or sever */
+   nbox = cell->npersist;
+   box = cell->persist;
+
+/* Assess the clustering of all the streaks */
+   for(i=0; i<MAXSIZE; i++) xusage[i] = 0;
+   for(k=0; k<nbox; k++) {
+      if(box[k].exfit >= MAXSIZE-1) continue;
+      for(i=box[k].sxfit; i<=box[k].exfit; i++) xusage[i] += 1;
+   }
+
+/* Identify clusters */
+   for(xs=0; xs<MAXSIZE-1; xs++) {
+      if(xusage[xs] == 0) continue;
+      for(xe=xs, lapmax=0; xe<MAXSIZE-1; xe++) {
+	 if(xusage[xe+1] == 0) break;
+	 lapmax = MAX(lapmax, xusage[xe]);
+      }
+      if(lapmax == 1) {	/* No overlap?  No problem. */
+	 xs = xe + 1;	/* Hop to next gap */
+	 continue;
+      }
+
+/* Which boxes overlap this cluster? */
+      for(k=n=0; k<nbox; k++) {
+	 if(box[k].sxfit > xe || box[k].exfit < xs) continue;
+	 boxid[n] = k;
+	 yctr[n] = (box[k].y0m+box[k].y1m+box[k].y0p+box[k].y0p+2) / 4;
+	 n++;
+      }
+
+/* Case 1: different y start => sever */
+      for(kp=0; kp<n; kp++) {
+	 k = boxid[kp];
+	 zk = box[k].zero[box[k].nfit/2];
+	 for(jp=kp+1; jp<n; jp++) {
+	    j = boxid[jp];
+	    zj = box[j].zero[box[j].nfit/2];
+	    if(ABS(yctr[jp]-yctr[kp]) > DIFFERENT_STREAK) {
+/* Trim back the feebler streak */
+	       if(zk > zj) {
+		  if(box[j].sxfit >= box[k].sxfit) 
+		     box[j].sxfit = MAX(box[j].sxfit, box[k].exfit+1);
+		  if(box[j].exfit <= box[k].exfit) 
+		     box[j].exfit = MIN(box[j].exfit, box[k].sxfit-1);
+	       } else {
+		  if(box[k].sxfit >= box[j].sxfit) 
+		     box[k].sxfit = MAX(box[k].sxfit, box[j].exfit+1);
+		  if(box[k].exfit <= box[j].exfit) 
+		     box[k].exfit = MIN(box[k].exfit, box[j].sxfit-1);
+	       }
+	    }
+	 }
+      }
+
+/* Case 2: pretty much the same y start => union */
+      for(kp=0; kp<n; kp++) {
+	 k = boxid[kp];
+	 for(jp=kp+1; jp<n; jp++) {
+	    j = boxid[jp];
+	    if(ABS(yctr[jp]-yctr[kp]) <= DIFFERENT_STREAK) {
+/* Merge k into j, trash k from further consideration */
+	       box[j].time = MAX(box[j].time, box[k].time);
+	       box[j].sx = MIN(box[j].sx, box[k].sx);
+	       box[j].ex = MAX(box[j].ex, box[k].ex);
+	       box[j].sy = MIN(box[j].sy, box[k].sy);
+	       box[j].ey = MAX(box[j].ey, box[k].ey);
+	       box[j].y0m = MIN(box[j].y0m, box[k].y0m);
+	       box[j].y0p = MAX(box[j].y0p, box[k].y0p);
+	       box[j].x0m = MIN(box[j].x0m, box[k].x0m);
+	       box[j].x0p = MAX(box[j].x0p, box[k].x0p);
+	       box[j].y1m = MIN(box[j].y1m, box[k].y1m);
+	       box[j].y1p = MAX(box[j].y1p, box[k].y1p);
+	       box[j].x1m = MIN(box[j].x1m, box[k].x1m);
+	       box[j].x1p = MAX(box[j].x1p, box[k].x1p);
+	       box[j].sxfit = MIN(box[j].sxfit, box[k].sxfit);
+	       box[j].exfit = MAX(box[j].exfit, box[k].exfit);
+
+	       box[k].exfit = box[k].sxfit - 1;
+	       yctr[kp] = -2 * DIFFERENT_STREAK;
+	    }
+	 }
+      }
+
+      xs = xe + 1;	/* Hop to next gap */
+
+   } /* Cluster loop */
+
+/* Excise the boxes which have been eradicated (sxfit > exfit) */
+   for(k=0; k<nbox; k++) {
+      if(box[k].sxfit > box[k].exfit) {
+	 for(j=k; j<nbox-1; j++) memcpy(box+j, box+j+1, sizeof(OBJBOX));
+	 nbox--;
+	 k--;
+      }
+   }
+   cell->npersist = nbox;
+
+
+   return(0);
+}
+
+
+/****************************************************************/
+/* persist_write(): Write all the persistence data for the next image */
+STATIC int persist_write(CELL *cell, const char *outfile)
+{
+   int i, k, j, err=0;
+   FILE *fp;
+
+   if(outfile == NULL) return(0);
+   if( (fp=fopen(outfile, "w")) == NULL) {
+      fprintf(stderr, "\rerror: cannot open '%s' for writing\n", outfile);
+      return(-1);
+   }
+
+/* Dump out all the burns for the next image... */
+/* FIXME: what to do about generic cell info? */
+   fprintf(fp,  "# Cell: %d  sky= %d   rms= %d   bias= %d\n", 
+	   cell[0].cell, cell[0].sky, cell[0].rms, cell[0].bias);
+   fprintf(fp, "#Cell time    cx  cy  max   sx  sy   ex  ey  y0m y0p y1m y1p x0m x0p x1m x1p     slope    zero\n");
+
+   for(j=0; j<MAXCELL; j++) {
+/* First: patched up persists */
+      for(k=0; k<cell[j].npersist; k++) {
+	 if(cell[j].persist[k].fiterr) continue;
+	 if(cell[j].persist[k].nfit <= 0) continue;
+	 fprintf(fp, "%3d %7d  %3d %3d %5d %3d  %3d %3d  %3d %3d  %3d %3d %3d %3d %3d %3d %3d %3d  %1d %1d %9.6f %3d %3d %3d\n",
+		 j, cell[j].persist[k].time, 
+		 cell[j].persist[k].cx, cell[j].persist[k].cy, 
+		 cell[j].persist[k].max, cell[j].persist[k].y0,
+		 cell[j].persist[k].sx, cell[j].persist[k].sy, 
+		 cell[j].persist[k].ex, cell[j].persist[k].ey,
+		 cell[j].persist[k].y0m, cell[j].persist[k].y0p,
+		 cell[j].persist[k].y1m, cell[j].persist[k].y1p,
+		 cell[j].persist[k].x0m, cell[j].persist[k].x0p,
+		 cell[j].persist[k].x1m, cell[j].persist[k].x1p,
+		 cell[j].persist[k].func, cell[j].persist[k].up, 
+		 cell[j].persist[k].slope, cell[j].persist[k].nfit,
+		 cell[j].persist[k].sxfit, cell[j].persist[k].exfit);
+	 for(i=0; i<cell[j].persist[k].nfit; i++) {
+	    fprintf(fp, "%3d %3d %8.4f\n", cell[j].persist[k].xfit[i], 
+		    cell[j].persist[k].yfit[i], cell[j].persist[k].zero[i]);
+	 }
+      }
+
+/* Second: new burns */
+      for(k=0; k<cell[j].nburn; k++) {
+	 if(!cell[j].burn[k].burned) continue;
+	 if(cell[j].burn[k].fiterr && 
+	    cell[j].burn[k].fiterr != FIT_TOP_ERROR) continue;
+	 if(cell[j].burn[k].nfit <= 0) continue;
+
+	 i = (cell[j].burn[k].ex - cell[j].burn[k].sx + 1) / 2;
+	 fprintf(fp, "%3d %7d  %3d %3d %5d %3d  %3d %3d  %3d %3d  %3d %3d %3d %3d %3d %3d %3d %3d  %1d %1d %9.6f %3d %3d %3d\n", 
+		 j, cell[j].burn[k].time, 
+		 cell[j].burn[k].cx, cell[j].burn[k].cy,
+		 cell[j].burn[k].max, cell[j].burn[k].y0,
+		 cell[j].burn[k].sx, cell[j].burn[k].sy, 
+		 cell[j].burn[k].ex, cell[j].burn[k].ey,
+		 cell[j].burn[k].y0m, cell[j].burn[k].y0p,
+		 cell[j].burn[k].y1m, cell[j].burn[k].y1p,
+		 cell[j].burn[k].x0m, cell[j].burn[k].x0p,
+		 cell[j].burn[k].x1m, cell[j].burn[k].x1p,
+		 cell[j].burn[k].func, cell[j].burn[k].up, 
+		 cell[j].burn[k].slope, cell[j].burn[k].nfit,
+		 cell[j].burn[k].sxfit, cell[j].burn[k].exfit);
+	 for(i=0; i<cell[j].burn[k].nfit; i++) {
+	    fprintf(fp, "%3d %3d %8.4f\n", cell[j].burn[k].xfit[i], 
+		    cell[j].burn[k].yfit[i], cell[j].burn[k].zero[i]);
+	 }
+      }
+   }
+   fclose(fp);
+
+   return(err);
+}
Index: /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/burntool/psfstamp.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/burntool/psfstamp.c	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/burntool/psfstamp.c	(revision 23594)
@@ -0,0 +1,374 @@
+/* psfstamp.c - identify and write PSF stars */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "math.h"
+
+#include "burntool.h"
+#include "burnparams.h"
+#include "pscoords/pscoords.h"
+
+#define PIXEL_FWHM 0.68	/* FWHM of one pixel: sqrt(ln(256)/12) */
+
+/****************************************************************/
+/* psf_select(): Choose worthy stars and copy a postage stamp */
+STATIC int psf_select(int nx, int ny, int NX, MTYPE *mask, DTYPE *data,
+		      int nbox, OBJBOX *box, int size, int sky)
+{
+   int i, j, k, sum;
+   int x0, x1, y0, y1, xm, xp, ym, yp;
+
+   for(k=0; k<nbox; k++) {
+      box[k].func = FUNC_NONE;
+
+/* Too small? */
+      if(VERBOSE & VERB_PSFSEL) {
+	 printf("Testing star at %d %d for max, %d\n", 
+		box[k].cx, box[k].cy, box[k].max);
+      }
+      if(box[k].max < PSF_THRESH) continue;
+
+/* If the max is very decentered in the box, choose the box */
+      if(ABS((box[k].sx+box[k].ex+1)/2 - box[k].cx) < PSF_CTR_TOL &&
+	 ABS((box[k].sy+box[k].ey+1)/2 - box[k].cy) < PSF_CTR_TOL) {
+	 x0 = box[k].cx - (size-1)/2;
+	 x1 = box[k].cx + size/2;
+	 y0 = box[k].cy - (size-1)/2;
+	 y1 = box[k].cy + size/2;
+	 box[k].up = box[k].cx;		/* Coopt for box center */
+	 box[k].y0 = box[k].cy;		/* Coopt for box center */
+      } else {
+	 x0 = (box[k].sx+box[k].ex+1)/2 - (size-1)/2;
+	 x1 = (box[k].sx+box[k].ex+1)/2 + size/2;
+	 y0 = (box[k].sy+box[k].ey+1)/2 - (size-1)/2;
+	 y1 = (box[k].sy+box[k].ey+1)/2 + size/2;
+	 box[k].up = (box[k].sx+box[k].ex+1)/2;		/* Coopt */
+	 box[k].y0 = (box[k].sy+box[k].ey+1)/2;		/* Coopt */
+      }
+
+/* Too close to the edge? */
+      if(VERBOSE & VERB_PSFSEL) {
+	 printf("testing for edge %d %d %d %d\n", x0, y0, x1, y1);
+      }
+      if(x0 < 0 || x1 > nx-1 || y0 < 0 || y1 > ny-1) continue;
+
+/* Box too big (blobby, extended crap)? */
+      if(VERBOSE & VERB_PSFSEL) {
+	 printf("testing for too big %d %d > %d ?\n", 
+	      box[k].ex-box[k].sx+1, box[k].ey-box[k].sy+1, size);
+      }
+      if(box[k].ex-box[k].sx+1 > size || 
+	 box[k].ey-box[k].sy+1 > size) continue;
+/* Box too small (CR?)? */
+      if(VERBOSE & VERB_PSFSEL) {
+	 printf("testing for too small %d %d < %d ?\n", 
+	      box[k].ex-box[k].sx+1, box[k].ey-box[k].sy+1, MIN_PSF_SIZE);
+      }
+      if(box[k].ex-box[k].sx+1 < MIN_PSF_SIZE || 
+	 box[k].ey-box[k].sy+1 < MIN_PSF_SIZE) continue;
+/* Overlap with mask?  Run around edge, outside of star box... */
+      ym = MIN(y0, box[k].sy-1);
+      yp = MAX(y1, box[k].ey+1);
+      if(ym < 0 || yp >= ny-1) {
+	 if(VERBOSE & VERB_PSFSEL) 
+	    printf("No edge below or above y %d %d\n", ym, yp);
+	 continue;
+      }
+      for(i=x0; i<=x1; i++) {
+	 if(mask[i+ym*NX] > MASK_STAR_HALO) break;
+	 if(mask[i+yp*NX] > MASK_STAR_HALO) break;
+      }
+      if(VERBOSE & VERB_PSFSEL) {
+	 printf("testing for too much mask overlap x %d < %d ?\n", i, x1);
+      }
+      if(i <= x1) continue;
+      xm = MIN(x0, box[k].sx-1);
+      xp = MAX(x1, box[k].ex+1);
+      if(xm < 0 || xp >= nx-1) {
+	 if(VERBOSE & VERB_PSFSEL) 
+	    printf("No edge left or right x %d %d\n", xm, xp);
+	 continue;
+      }
+      for(j=y0+1; j<y1; j++) {
+	 if(mask[xm+j*NX] > MASK_STAR_HALO) break;
+	 if(mask[xp+j*NX] > MASK_STAR_HALO) break;
+      }
+      if(VERBOSE & VERB_PSFSEL) {
+	 printf("testing for too much mask overlap y, %d < %d ?\n", j, y1);
+      }
+      if(j < y1) continue;
+
+/* Good star */
+      if(VERBOSE & VERB_PSFSEL) {
+	 printf("good star at %d %d...\n", box[k].up, box[k].y0);
+      }
+      box[k].func = PSF_STAR;
+
+/* Make a postage stamp for it */
+      box[k].stamp = (IMTYPE *)calloc(size*size, sizeof(IMTYPE));
+      sum = 0;
+      for(j=y0; j<=y1; j++) {
+	 for(i=x0; i<=x1; i++) {
+	    box[k].stamp[(i-x0)+(j-y0)*size] = data[i+j*NX] - sky +
+	       USHORT_BIAS - BZERO;
+	    sum += data[i+j*NX] - sky;
+	 }
+      }
+      box[k].diff = sum;
+      box[k].slope = MAX(0.1, sum) / box[k].max;
+   }
+
+   return(0);
+}
+
+
+/****************************************************************/
+/* psf_write() writes all the postage stamps to a 3D FITS file */
+STATIC int psf_write(int nx, int ny, CELL ota[], int otanum, 
+		     const char *psffile)
+{
+   int i, k, l, nstar, fdout, otacx, otacy, xid, yid, ntot=0, sumax;
+   int cellcount, ota_xid, ota_yid;
+   double scale, phi, fwhm[3], q[3], qt, xfp, yfp, pi=4*atan(1.0);
+   IMTYPE *median_image;
+   CELL *cell;
+   OBJBOX *box;
+
+/* How many do we have to write? */
+   nstar = 0;
+   for(k=0; k<MAXCELL; k++) {
+      cell = ota + k;
+      for(l=cellcount=0; l<cell->nstar; l++) {
+	 box = cell->star+l;
+	 if(box->func != PSF_STAR) continue;
+         if(cellcount++ > MAX_PSF_PER_CELL) break;
+	 if(nstar < nmedian_buf) median_buf[nstar] = box->slope;
+	 nstar++;
+      }
+   }
+   if(nstar == 0) {
+      printf("N= %d\n", nstar);
+      return(0);
+   }
+
+/* Use the wicked crude cheapfits.c */
+   if((fdout=creat(psffile, 0644)) < 0) {
+      fprintf(stderr,"error: cannot open output PSF file %s\n", psffile);
+      return(-1);
+   }
+
+   if(!CONCAT_FITS) write_3dhdr(nx, ny, nstar+1, 0, 0, fdout);
+
+/* Median sum/max */
+   sumax = int_median(MIN(nstar, nmedian_buf), median_buf);
+//   printf("sumax = %d\n", sumax);
+
+/* Create a median image and write as the first one */
+   median_image = (IMTYPE *)calloc(nx*ny, sizeof(IMTYPE));
+   for(i=0; i<nx*ny; i++) {
+      nstar = 0;
+      for(k=0; k<MAXCELL; k++) {
+	 cell = ota + k;
+	 for(l=cellcount=0; l<cell->nstar; l++) {
+	    box = cell->star+l;
+	    if(box->func != PSF_STAR) continue;
+	    if(cellcount++ > MAX_PSF_PER_CELL) break;
+	    scale = 1e4 * sumax / box->diff;
+	    median_buf[nstar++] = (box->stamp[i]-USHORT_BIAS+BZERO) * scale;
+	 }
+      }
+      median_image[i] = int_median(nstar, median_buf) + 
+	 USHORT_BIAS - BZERO;
+   }
+
+   i = psf_stats(nx, ny, median_image, USHORT_BIAS-BZERO, fwhm, q);
+
+/* Subtract out one pixel in quadrature from median */
+   if(fwhm[0] > PIXEL_FWHM && fwhm[1] > PIXEL_FWHM) {
+      fwhm[0] = sqrt(fwhm[0]*fwhm[0] - PIXEL_FWHM*PIXEL_FWHM);
+      fwhm[1] = sqrt(fwhm[1]*fwhm[1] - PIXEL_FWHM*PIXEL_FWHM);
+   }
+
+   ota_xid = otanum % 8;
+   ota_yid = otanum / 8;
+   psc_pixel_to_fp(ota_xid, ota_yid, 2423.0, 2434.0, &xfp, &yfp);
+   phi = atan2(yfp, xfp);
+/* Change so that + means tangential, - means radial */
+   qt = -q[1] * cos(2*phi) - q[2] * sin(2*phi);
+
+   printf("N= %d PSFmaj= %.2f min= %.2f theta= %.1f m2= %.2f q+= %.3f qx= %.3f qt= %.3f\n",
+	  nstar, fwhm[0], fwhm[1], fwhm[2]*180/pi, q[0], q[1], q[2], qt);
+
+   if(CONCAT_FITS) {
+      write_2dfits(nx, ny, 0, 0, median_image, fdout);
+   } else {
+      write_2ddata(nx, ny, &ntot, median_image, fdout);
+   }
+
+/* Write each stamp as concatenated or 3D FITS file */
+   nstar = 0;
+   for(k=0; k<MAXCELL; k++) {
+      cell = ota + k;
+      xid = k % 8;
+      yid = k / 8;
+      for(l=cellcount=0; l<cell->nstar; l++) {
+	 box = cell->star+l;
+	 if(box->func != PSF_STAR) continue;
+	 if(cellcount++ > MAX_PSF_PER_CELL) break;
+	 nstar++;
+/* Note that x flip required the bigger side offset when nx is even */
+	 otacx = (PSC_HCELL - box->up) + xid*(PSC_HCELL+PSC_VSTREET);
+	 otacy = box->y0 + yid*(PSC_VCELL+PSC_HSTREET);
+	 if(CONCAT_FITS) {
+	    write_2dfits(nx, ny, otacx, otacy, box->stamp, fdout);
+	 } else {
+/* Nahhh, nobody likes this but me... */
+//	    box->stamp[0] = otacx-BZERO;	/* Origin is first 2 pixels */
+//	    box->stamp[1] = otacy-BZERO;
+	    write_2ddata(nx, ny, &ntot, box->stamp, fdout);
+	 }
+
+      }
+   }
+   if(!CONCAT_FITS) write_3dend(&ntot, fdout);
+
+   close(fdout);
+   free(median_image);
+   return(0);
+}
+
+#define MAXPSFMEDIAN (20*64)		/* Max PSF's per OTA */
+#define MIN_BELIEVABLE_FWHM 2.0		/* Minimum FWHM we'll accept */
+
+/****************************************************************/
+/* psf_write_stats() writes average PSF stats for all the cells */
+STATIC int psf_write_stats(int nx, int ny, CELL ota[], int otanum, 
+			   const char *statfile, int psfavg)
+{
+   int i0, j0, i, j;
+   int k, l, cellx, celly, ota_xid, ota_yid, nfwave, nqavg;
+   double xota, yota, xfp, yfp, phi;
+   double fwhm[3], q[3], fw[MAXPSFMEDIAN];
+   double m2[MAXPSFMEDIAN], qp[MAXPSFMEDIAN], qc[MAXPSFMEDIAN];
+   double qt[MAXPSFMEDIAN], fwavg[MAXPSFMEDIAN];
+   double qpavg[MAXPSFMEDIAN], qcavg[MAXPSFMEDIAN], qtavg[MAXPSFMEDIAN];
+   int nstar[MAXCELL], nfw[MAXCELL];
+   double fwmed[MAXCELL], m2med[MAXCELL];
+   double qpmed[MAXCELL], qcmed[MAXCELL], qtmed[MAXCELL];
+   double qpmacro, qcmacro, qtmacro, fwmacro;
+   FILE *fp;
+   CELL *cell;
+   OBJBOX *box;
+
+/* Convert psfavg from log2(N) */
+   if(psfavg >= 3) psfavg = 8;
+   else if(psfavg == 2) psfavg = 4;
+   else if(psfavg == 1) psfavg = 2;
+   else psfavg = 1;
+
+   ota_xid = otanum % PSC_NX;
+   ota_yid = otanum / PSC_NX;
+
+/* Open the output file */
+   if( (fp=fopen(statfile, "w")) == NULL) {
+      fprintf(stderr, "error: psf_write_stats cannot open '%s' for writing\n",
+	      statfile);
+      return(-1);
+   }
+
+/* Loop over all the macro-cells */
+   for(j0=0; j0<PSC_NY; j0+=psfavg) {
+      for(i0=0; i0<PSC_NX; i0+=psfavg) {
+	 nfwave = nqavg = 0;
+/* Loop over the constituent cells */
+	 for(j=0; j<psfavg; j++) {
+	    for(i=0; i<psfavg; i++) {
+	       cellx = i0 + i;
+	       celly = j0 + j;
+	       k = PSC_NX*celly + cellx;
+	       cell = ota + k;
+	       nstar[k] = nfw[k] = 0;
+/* Generate average statistics for all the PSF stars */
+	       for(l=0; l<cell->nstar; l++) {
+		  box = cell->star+l;
+		  if(box->func != PSF_STAR) continue;
+		  psf_stats(nx, ny, box->stamp, USHORT_BIAS-BZERO, fwhm, q);
+		  if(nstar[k] < MAXPSFMEDIAN) {
+		     m2[nstar[k]] = q[0];
+		     qp[nstar[k]] = q[1];
+		     qc[nstar[k]] = q[2];
+/* Get the position in the focal plane and therefore the qt statistic */
+		     psc_cell_to_pixel(cellx, celly, 0.5*PSC_HCELL/PSC_PIXEL, 
+				       0.5*PSC_VCELL/PSC_PIXEL, &xota, &yota);
+		     psc_pixel_to_fp(ota_xid, ota_yid,	xota, yota, &xfp, &yfp);
+		     phi = atan2(yfp, xfp);
+/* Change so that + means tangential, - means radial */
+		     qt[nstar[k]] = -q[1] * cos(2*phi) - q[2] * sin(2*phi);
+		     nstar[k] += 1;
+		  }
+		  if(fwhm[0] > MIN_BELIEVABLE_FWHM && 
+		     fwhm[1] > MIN_BELIEVABLE_FWHM && nfw[k] < MAXPSFMEDIAN) {
+		     fw[nfw[k]] = sqrt(fwhm[0]*fwhm[1]);
+		     nfw[k] += 1;
+		  }
+	       }
+	       fwmed[k] = double_median(nfw[k], fw);
+	       if(nstar[k] > 0) {
+		  m2med[k] = double_median(nstar[k], m2);
+		  qpmed[k] = double_median(nstar[k], qp);
+		  qcmed[k] = double_median(nstar[k], qc);
+		  qtmed[k] = double_median(nstar[k], qt);
+	       } else {
+		  m2med[k] = qpmed[k] = qcmed[k] = qtmed[k] = -99.99;
+	       }
+/* Toss these results into the macrocell median hopper */
+	       for(l=0; l<nfw[k]; l++) {
+		  if(nfwave < MAXPSFMEDIAN) fwavg[nfwave++] = fw[l];
+	       }
+	       for(l=0; l<nstar[k]; l++) {
+		  if(nqavg < MAXPSFMEDIAN) {
+		     qpavg[nqavg] = qp[l];
+		     qcavg[nqavg] = qc[l];
+		     qtavg[nqavg] = qt[l];
+		     nqavg++;
+		  }
+	       }
+	    }
+	 }
+/* Get the median of FWHM and qt over this macrocell */
+	 qpmacro = double_median(nqavg, qpavg);
+	 qcmacro = double_median(nqavg, qcavg);
+	 qtmacro = double_median(nqavg, qtavg);
+	 if(nqavg == 0) qpmacro = qcmacro = qtmacro = -99.99;
+	 fwmacro = double_median(nfwave, fwavg);
+
+/* Print out the results from this macrocell */
+	 for(j=0; j<psfavg; j++) {
+	    for(i=0; i<psfavg; i++) {
+	       cellx = i0 + i;
+	       celly = j0 + j;
+	       k = PSC_NX*celly + cellx;
+	       cell = ota + k;
+
+	       fprintf(fp, "ext=xy%1d%1d bias=%d sky=%d rmssky=%d npsf=%d fwhm=%.2f fwmed=%.2f m2=%.2f qp=%.3f qc=%.3f qt=%.3f qpm=%.3f qcm=%.3f qtm=%.3f\n", 
+		       cellx, celly, cell->bias, cell->sky, cell->rms, 
+		       nstar[k], fwmed[k], fwmacro, m2med[k], 
+		       qpmed[k], qcmed[k], qtmed[k], 
+		       qpmacro, qcmacro, qtmacro);
+	    }
+	 }
+
+
+
+      } /* i0 */
+   } /* j0 */
+
+   fclose(fp);
+   return(0);
+}
Index: /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/burntool/psfstats.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/burntool/psfstats.c	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/burntool/psfstats.c	(revision 23594)
@@ -0,0 +1,124 @@
+/* psfstats.c - calculate some statistics about a PSF */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "burntool.h"
+#include "math.h"
+#include "psf/psf.h"
+
+//#define PSFSTATS		/* ignore psf stats so we can skip fortan */
+//#define TEST		/* Dump all the PSF info? */
+
+static unsigned short int *psfbuf=NULL;
+static int npsfbuf=0;
+#define PHONYSKY 1000
+
+/****************************************************************/
+/* psf_stats(): Tell us about this PSF star */
+STATIC int psf_stats(int nx, int ny, IMTYPE *data, int bias, 
+		     double *fwhm, double *q)
+{
+
+#ifdef PSFSTATS
+
+   int i, j, r, r2, err, i0, j0;
+   double d, qxx, qxy, qyy, mx, my, sum;
+   PSF_PARAM psfout;      /* Results of fit */
+   PSF_EXTRA psfextra;    /* Extra results from fit */
+   double pi=4*atan(1.0);
+
+   if(nx*ny > npsfbuf) {
+      if(psfbuf != NULL) free(psfbuf);
+      psfbuf = (ushort *)calloc(nx*ny, sizeof(ushort));
+      npsfbuf = nx*ny;
+   }
+   for(i=0; i<nx*ny; i++) {
+      j = ((int)data[i]) - bias + PHONYSKY;
+      if(j >= 0 && j < 65535) psfbuf[i] = j;
+      else psfbuf[i] = 0;
+   }
+
+/* 2D PSF fit */
+   err = psf(nx, ny, psfbuf, PSF_2DIM, NULL, &psfout, &psfextra);
+
+#ifdef TEST
+   printf("err = %d\n", err);
+#endif
+
+   if(!err) {
+#ifdef TEST
+      printf("err = %d\n", err);
+      printf("bias = %d\n", bias);
+      printf("ix, iy = %d %d\n", psfout.ix, psfout.iy);
+      printf("x0, y0 = %.2f %.2f\n", psfout.x0, psfout.y0);
+      printf("fwhm = %.2f\n", psfout.fwhm);
+      printf("peak, bckgnd = %.1f %.1f\n", psfout.peak, psfout.bkgnd);
+      printf("flux, S/N = %.1f %.2f\n", psfout.flux, psfout.sn);
+      printf("weight = %.2f\n", psfout.weight);
+      printf("FWmaj, FWmin = %.2f %.2f\n", psfextra.majfw, psfextra.minfw);
+      printf("theta = %.1f\n", psfextra.thfw*180/pi);
+      printf("wpeak, wbkgnd = %.1f %.1f\n", psfextra.wpeak, psfextra.wbkgnd);
+      printf("dflux, dbckgnd = %.1f %.1f\n", psfextra.dflux, psfextra.dbkgnd);
+      printf("rmsbkgnd = %.1f\n", psfextra.rmsbkgnd);
+#endif
+      fwhm[0] = psfextra.majfw;
+      fwhm[1] = psfextra.minfw;
+      fwhm[2] = psfextra.thfw;
+
+/* Moments */
+      i0 = MIN(psfout.ix, nx-psfout.ix);
+      j0 = MIN(psfout.iy, ny-psfout.iy);
+   } else {
+      fwhm[0] = fwhm[1] = fwhm[2] = 0.0;
+      i0 = nx/2;
+      j0 = ny/2;
+   }
+
+   r = MIN(i0, j0) - 1;
+   sum = mx = my = qxx = qxy = qyy = 0.0;
+   for(j=j0-r; j<j0+r; j++) {
+      for(i=i0-r; i<i0+r; i++) {
+	 r2 = (i-i0)*(i-i0) + (j-j0)*(j-j0);
+	 if(r2 > r*r) continue;
+	 d = (double)data[i+j*nx] - bias;
+	 sum += d;
+	 mx += (i+0.5) * d;
+	 my += (j+0.5) * d;
+	 qxx += (i+0.5) * (i+0.5) * d;
+	 qxy += (i+0.5) * (j+0.5) * d;
+	 qyy += (j+0.5) * (j+0.5) * d;
+      }
+   }
+   if(sum > 0) {
+      mx /= sum;
+      my /= sum;
+      qxx = qxx / sum - mx*mx;
+      qxy = qxy / sum - mx*my;
+      qyy = qyy / sum - my*my;
+   } else {
+      q[0] = q[1] = q[2] = 0.0;
+   }
+
+   q[0] = (qxx + qyy) / 2;
+   q[1] = qxx - qyy;
+   q[2] = 2 * qxy;
+
+/* phi is the angular position of the OTA around the center [rad] */
+//   qtang = (qxx - qyy) * cos(2*phi) + 2 * qxy * sin(2*phi);
+
+#ifdef TEST
+   printf("Sum= %.1f mx= %.2f my= %.2f qxx= %.4f qxy= %.4f qyy= %.4f\n", 
+	  sum, mx, my, qxx, qxy, qyy);
+#endif
+
+# else
+   printf("psfstats ignored in this build\n");
+   return (1);
+#endif /* PSFSTATS */
+   return(0);
+}
Index: /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/burntool/sort.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/burntool/sort.c	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/burntool/sort.c	(revision 23594)
@@ -0,0 +1,196 @@
+/* A few sorting routines */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define MAXSTACK 256
+//#define NSTOP 15
+#define NSTOP 8
+
+/* Quicksort program 1980 (John Tonry) */
+int qsort_dbl(int n, double *x)
+{
+  double key, kl, kr, km, temp;
+  int l, r, m, lstack[MAXSTACK], rstack[MAXSTACK], sp;
+  register int i, j, k;
+  int mgtl, lgtr, rgtm;
+
+  sp = 0;
+  lstack[sp] = 0;
+  rstack[sp] = n-1;
+
+  while(n > NSTOP && sp >= 0) {
+/* Sort a subrecord off the stack */
+    l = lstack[sp];
+    r = rstack[sp];
+    sp--;
+    m = (l + r) / 2;
+/* Set KEY = median of X(L), X(M), X(R) */
+    kl = x[l];
+    km = x[m];
+    kr = x[r];
+    mgtl = km > kl;
+    rgtm = kr > km;
+    lgtr = kl > kr;
+#ifndef STD_KEY
+    if(mgtl ^ rgtm) {
+      if(mgtl ^ lgtr) key = kr;
+      else            key = kl;
+    } else {
+      key = km;
+    }
+#else
+/* Curiously enough, this non-median seems to work as well or better */
+    if(mgtl ^ rgtm) {
+      key = km;
+    } else {
+      if(mgtl ^ lgtr) key = kl;
+      else            key = kr;
+    }
+#endif
+    i = l;
+    j = r;
+    while(1) {
+/* Find a big record on the left */
+      while(x[i] < key) i++;
+
+/* Find a small record on the right */
+      while(x[j] > key) j--;
+
+      if(i >= j) break;
+/* Exchange records */
+      temp = x[i];
+      x[i] = x[j];
+      x[j] = temp;
+      i++;
+      j--;
+    }
+
+/* Subfile is partitioned into two halves, left .le. right */
+/* Push the two halves on the stack */
+    if(j-l+1 > NSTOP) {
+      sp = sp + 1;
+      lstack[sp] = l;
+      rstack[sp] = j;
+    }
+    if(r-j > NSTOP) {
+      sp = sp + 1;
+      lstack[sp] = j+1;
+      rstack[sp] = r;
+    }
+    if(sp >= MAXSTACK) {
+//      fprintf(stderr,"QSORT8: Fatal error from stack overflow\n");
+//      fprintf(stderr,"Fall back on sort by insertion\n");
+      break;
+    }
+  }
+
+/* Sorting routine that sorts the N elements of single precision */
+/* array X by straight insertion between previously sorted numbers */
+  for(j=n-2; j>=0; j--) {
+    for(i=j+1, k=j; i<n; i++) {
+      if(x[j] <= x[i]) break;
+      k = i;
+    }
+    if(k != j) {
+      temp = x[j];
+      for(i=j+1; i<=k; i++) x[i-1] = x[i];
+      x[k] = temp;
+    }
+  }
+  return(0);
+}
+
+/* Quicksort program 1980 (John Tonry) */
+int qsort_int(int n, int *x)
+{
+  int key, kl, kr, km, temp;
+  int l, r, m, lstack[MAXSTACK], rstack[MAXSTACK], sp;
+  register int i, j, k;
+  int mgtl, lgtr, rgtm;
+
+  sp = 0;
+  lstack[sp] = 0;
+  rstack[sp] = n-1;
+
+  while(n > NSTOP && sp >= 0) {
+/* Sort a subrecord off the stack */
+    l = lstack[sp];
+    r = rstack[sp];
+    sp--;
+    m = (l + r) / 2;
+/* Set KEY = median of X(L), X(M), X(R) */
+    kl = x[l];
+    km = x[m];
+    kr = x[r];
+    mgtl = km > kl;
+    rgtm = kr > km;
+    lgtr = kl > kr;
+#ifndef STD_KEY
+    if(mgtl ^ rgtm) {
+      if(mgtl ^ lgtr) key = kr;
+      else            key = kl;
+    } else {
+      key = km;
+    }
+#else
+/* Curiously enough, this non-median seems to work as well or better */
+    if(mgtl ^ rgtm) {
+      key = km;
+    } else {
+      if(mgtl ^ lgtr) key = kl;
+      else            key = kr;
+    }
+#endif
+    i = l;
+    j = r;
+    while(1) {
+/* Find a big record on the left */
+      while(x[i] < key) i++;
+
+/* Find a small record on the right */
+      while(x[j] > key) j--;
+
+      if(i >= j) break;
+/* Exchange records */
+      temp = x[i];
+      x[i] = x[j];
+      x[j] = temp;
+      i++;
+      j--;
+    }
+
+/* Subfile is partitioned into two halves, left .le. right */
+/* Push the two halves on the stack */
+    if(j-l+1 > NSTOP) {
+      sp = sp + 1;
+      lstack[sp] = l;
+      rstack[sp] = j;
+    }
+    if(r-j > NSTOP) {
+      sp = sp + 1;
+      lstack[sp] = j+1;
+      rstack[sp] = r;
+    }
+    if(sp >= MAXSTACK) {
+//      fprintf(stderr,"QSORT8: Fatal error from stack overflow\n");
+//      fprintf(stderr,"Fall back on sort by insertion\n");
+      break;
+    }
+  }
+
+/* Sorting routine that sorts the N elements of single precision */
+/* array X by straight insertion between previously sorted numbers */
+  for(j=n-2; j>=0; j--) {
+    for(i=j+1, k=j; i<n; i++) {
+      if(x[j] <= x[i]) break;
+      k = i;
+    }
+    if(k != j) {
+      temp = x[j];
+      for(i=j+1; i<=k; i++) x[i-1] = x[i];
+      x[k] = temp;
+    }
+  }
+  return(0);
+}
Index: /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/burntool/stardetect.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/burntool/stardetect.c	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/burntool/stardetect.c	(revision 23594)
@@ -0,0 +1,200 @@
+/* stardetect.c - identify burns and stars */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "math.h"
+
+#include "burntool.h"
+#include "burnparams.h"
+
+/****************************************************************/
+/* star_detect(): Find all the stars and burned patches in a cell */
+STATIC int star_detect(int nx, int ny, int NX, int NY, DTYPE *data,
+		       MTYPE *mask, MTYPE *veto, CELL *cell, int cellnum)
+{
+   int i, j, k, l;
+   int burnthresh, trailthresh, starthresh, starcut, thresh_hi, thresh_lo;
+   int nbox, xon, xoff;
+
+/* Reset the masks (DEPENDS on MASK_NONE = 0, tough!) */
+   bzero(mask, NX*ny*sizeof(MTYPE));
+   bzero(veto, NX*ny*sizeof(MTYPE));
+
+   nbox = 0;
+
+   burnthresh = thresh_hi = BURN_THRESH + cell->bias;
+   trailthresh = thresh_lo = TRAIL_THRESH + cell->bias;
+   starthresh = STAR_THRESH + cell->sky + cell->bias;
+   starcut = STAR_THRESH/2 + cell->sky + cell->bias;
+
+/* Look at all the pixels which pass the burn threshold */
+   for(j=0; j<ny; j++) {
+      xon = -1;
+      for(i=0; i<nx; i++) {
+/* Big enough?  Initialize a box and push it outwards to the trail level. */
+	 if(mask[i+j*NX] > MASK_NONE) continue;
+	 if(xon < 0) {
+	    if(data[i+j*NX] > burnthresh) {
+	       xon = i;
+	       thresh_hi = burnthresh;
+	       thresh_lo = trailthresh;
+	       if(VERBOSE & VERB_DETECT) {
+		  printf("Starting a burn at %d %d %d\n", i, j, data[i+j*NX]);
+	       }
+	    } else if(data[i+j*NX] > starthresh) {
+/* Local maximum? */
+	       if(!local_max(i, j, STAR_RADIUS, nx, ny, NX, data)) continue;
+	       if(data[i+j*NX] < veto[i+j*NX]) continue;
+	       xon = i;
+//	       thresh_hi = starthresh;
+//	       thresh_lo = starcut;
+	       thresh_hi = data[i+j*NX];
+	       thresh_lo = STAR_FRAC*(data[i+j*NX]-cell->sky-cell->bias) + 
+		  cell->sky + cell->bias;
+	       if(VERBOSE & VERB_DETECT) {
+		  printf("Starting a star at %d %d %d\n", i, j, data[i+j*NX]);
+	       }
+	    }
+	 }
+
+	 if(xon >= 0 && (data[i+j*NX] < thresh_hi || i == nx-1)) {
+
+//	 if(xon < 0 && data[i+j*NX] > burnthresh && mask[i+j*NX] == 0) xon = i;
+//	 if(xon > 0 && (data[i+j*NX] < burnthresh || i == nx-1)) {
+
+	    xoff = i<nx-1 ? i-1 : nx-1;
+	    if(nbox >= MAXBURN) {
+	       fprintf(stderr, "error: too many burn boxes\n");
+	       return(-1);
+	    }
+	    boxbuf[nbox].sx = xon;
+	    boxbuf[nbox].ex = xoff;
+	    boxbuf[nbox].sy = j;
+	    for(k=j; k<ny && data[(xon+xoff)/2+k*NX] > thresh_hi; k++);
+	    boxbuf[nbox].ey = k-1;
+	    grow_box(nx, ny, NX, data, thresh_lo, boxbuf+nbox);
+/* Fill in max and center info */
+	    boxbuf[nbox].max = 0;
+	    for(l=boxbuf[nbox].sy; l<=boxbuf[nbox].ey; l++) {
+	       for(k=boxbuf[nbox].sx; k<=boxbuf[nbox].ex; k++) {
+		  if(data[k+l*NX] > boxbuf[nbox].max) {
+		     boxbuf[nbox].cx = k;
+		     boxbuf[nbox].cy = l;
+		     boxbuf[nbox].max = data[k+l*NX];
+		  }
+	       }
+	    }
+/* A box which triggered on starthresh cannot envelop burnthresh */
+	    if(thresh_hi < burnthresh && boxbuf[nbox].max > burnthresh) {
+	       if(VERBOSE & VERB_DETECT) {
+		  printf("Ditching box %d %d %d %d  max = %d > burnthresh\n",
+			 boxbuf[nbox].sx, boxbuf[nbox].sy,
+			 boxbuf[nbox].ex, boxbuf[nbox].ey,
+			 boxbuf[nbox].max);
+	       }
+/* Mark the veto mask so as not to trigger on this one again */
+	       for(l=boxbuf[nbox].sy; l<=boxbuf[nbox].ey; l++) {
+		  for(k=boxbuf[nbox].sx; k<=boxbuf[nbox].ex; k++) {
+		     veto[k+l*NX] = boxbuf[nbox].max;
+		  }
+	       }
+	       xon = -1;
+	       continue;
+	    }
+
+/* Mask the pixels so as not to catch them again! */
+	    for(l=boxbuf[nbox].sy; l<=boxbuf[nbox].ey; l++) {
+	       for(k=boxbuf[nbox].sx; k<=boxbuf[nbox].ex; k++) {
+		  mask[k+l*NX] = MASK_CTR;
+	       }
+	    }
+	    boxbuf[nbox].sat = boxbuf[nbox].max >= burnthresh;
+	    boxbuf[nbox].max -= cell->sky + cell->bias;
+/* Burned only if it triggered on burnthresh, not if it enveloped it */
+	    boxbuf[nbox].burned = thresh_hi >= burnthresh;
+/* But take a hard look at stars with really bright centers... */
+	    if(!boxbuf[nbox].burned && boxbuf[nbox].max > MAX_THRESH) {
+	       burn_test(nx, ny, NX, data, cell->rms, mask, boxbuf+nbox);
+	    }
+	    boxbuf[nbox].time = cell->time;
+	    boxbuf[nbox].cell = cellnum;
+	    boxbuf[nbox].stamp = NULL;
+	    boxbuf[nbox].func = FUNC_NONE;
+	    boxbuf[nbox].sxfit = boxbuf[nbox].sx;
+	    boxbuf[nbox].exfit = boxbuf[nbox].ex;
+	    boxbuf[nbox].nfit = 0;
+	    boxbuf[nbox].zero = NULL;
+	    boxbuf[nbox].xfit = boxbuf[nbox].yfit = NULL;
+	    if(VERBOSE & VERB_DETECT) {
+	       printf("New box at %d %d %d %d", 
+		      boxbuf[nbox].sx, boxbuf[nbox].sy,boxbuf[nbox].ex, boxbuf[nbox].ey);
+	       printf("   %d %d %d %d", 
+		      boxbuf[nbox].x0m, boxbuf[nbox].x0p,boxbuf[nbox].x1m, boxbuf[nbox].x1p);
+	       printf("   %d %d %d %d\n", 
+		      boxbuf[nbox].y0m, boxbuf[nbox].y0p,boxbuf[nbox].y1m, boxbuf[nbox].y1p);
+	    }
+	    xon = -1;
+	    nbox++;
+	 }
+      }
+   }
+
+/* Reject any stars which have enveloped a burned core */
+   for(k=0; k<nbox; k++) {
+//      printf("Examining box %d\n", k);
+      if(!(boxbuf[k].burned)) continue;
+//      printf("  which is a burn\n");
+      for(l=0; l<nbox; l++) {
+	 if(k == l) continue;
+//	 printf("Examining star %d\n", l);
+/* Has star l enveloped burn k? */
+/* Don't care whether this is burned or unburned: enveloping is bad */
+//	 if(!(boxbuf[l].burned)) continue;
+//	 printf("  which has a burned core\n");
+//	 printf("%3d %3d %3d %3d %3d %3d %3d %3d\n", 
+//		boxbuf[k].sx, boxbuf[l].sx, boxbuf[k].ex, boxbuf[l].ex,
+//		boxbuf[k].sy, boxbuf[l].sy, boxbuf[k].ey, boxbuf[l].ey);
+	 if(boxbuf[k].sx >= boxbuf[l].sx &&
+	    boxbuf[k].ex <= boxbuf[l].ex &&
+	    boxbuf[k].sy >= boxbuf[l].sy &&
+	    boxbuf[k].ey <= boxbuf[l].ey) {
+/* Wipe it out */
+	    for(i=l; i<nbox-1; i++) memcpy(boxbuf+i, boxbuf+i+1, sizeof(OBJBOX));
+//	    printf("Wiping out star %d enveloping burn %d\n", l, k);
+	    nbox--;
+	    l--;
+	    if(k > l) k--;
+	 }
+      }
+   }
+
+/* Remark mask if saturated */
+   for(i=0; i<nbox; i++) {
+      if(boxbuf[i].sat) {
+	 for(l=boxbuf[i].sy; l<=boxbuf[i].ey; l++) {
+	    for(k=boxbuf[i].sx; k<=boxbuf[i].ex; k++) {
+	       mask[k+l*NX] = MASK_SAT;
+	    }
+	 }
+      }
+   }
+
+/* How many real burns? */
+   for(k=cell->nburn=0; k<nbox; k++) if(boxbuf[k].burned) cell->nburn++;
+   cell->nstar = nbox - cell->nburn;
+   cell->burn = (OBJBOX *)calloc(cell->nburn, sizeof(OBJBOX));
+   cell->star = (OBJBOX *)calloc(cell->nstar, sizeof(OBJBOX));
+
+/* Copy the boxes to the cell info structure */
+   for(k=i=j=0; k<nbox; k++) {
+      if(boxbuf[k].burned) memcpy(cell->burn+j++, boxbuf+k, sizeof(OBJBOX));
+      else                 memcpy(cell->star+i++, boxbuf+k, sizeof(OBJBOX));
+   }
+   return(0);
+}
Index: /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/burntool/trailfit.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/burntool/trailfit.c	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/burntool/trailfit.c	(revision 23594)
@@ -0,0 +1,281 @@
+/* trailfit.c - routines to fit and subtract trails */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "math.h"
+
+#include "burntool.h"
+#include "burnparams.h"
+
+STATIC double ybuf[MAXSIZE], zbuf[MAXSIZE], wbuf[MAXSIZE];
+
+/****************************************************************/
+/* Subtract one rectangle's fit from the data */
+STATIC int sub_fit(int nx, int ny, int NX, IMTYPE *buf, OBJBOX *box, 
+		   int sign)
+{
+   int i, j, k;
+   int y0, y1, ys, dy, delta;
+
+   if(ny > MAXSIZE) {
+      fprintf(stderr, "error: not enough buffer in fit_trail\n");
+      return(-1);
+   }
+
+   if(box->func != BURN_PWR && box->func != BURN_EXP) {
+      fprintf(stderr, "error: unimplemented fit function %d\n", box->func);
+      return(-1);
+   }
+
+   if(box->fiterr) return(-1);
+   y0 = box->y0;
+   y1 = (box->up) ? ny-1 : 0;
+   dy = (y0<y1) ? +1 : -1;
+   if(box->up) ys = box->sy;
+   else        ys = box->ey;
+
+/* Calculate all needed y values */
+   for(j=ys; dy*j<=dy*y1; j+=dy) {
+      if(box->func == BURN_PWR) {
+	 if(dy*(j-y0) <= 0) ybuf[j] = exp(box->slope*log(Y_SCALE*1.0));
+	 else ybuf[j] = exp(box->slope*log(Y_SCALE*dy*(j-y0)));
+      } else if(box->func == BURN_EXP) {
+//	 ybuf[j] = exp(box->slope*Y_SCALE*MAX(0,dy*(j-y0)));
+	 ybuf[j] = exp(box->slope*Y_SCALE*dy*(j-y0));
+      }
+   }
+/* Subtract the fits */
+//   printf("%3d %3d %3d %1d %1d %8.5f\n", 
+//	  box->cx, box->cy, box->y0, box->func, box->up, box->slope);
+
+   for(k=0; k<box->nfit; k++) {
+      i = box->xfit[k];
+      if(sign > 0) {		/* Subtract */
+	 for(j=box->yfit[k]; dy*j<=dy*y1; j+=dy) {
+//	 if(k==box->nfit/2) printf("%3d %6d ", j, buf[i+NX*j]);
+	    delta = box->zero[k] * ybuf[j] + 0.5;
+/* No 16 bit wraparound below zero allowed */
+//	    if(delta < buf[i+NX*j]+BZERO) buf[i+NX*j] -= delta;
+/* Allow 16 bit wraparound in order to undo it... */
+	    buf[i+NX*j] -= delta;
+//	 if(k==box->nfit/2) printf("%6.1f %6d\n", 
+//			 sign * box->zero[k] * ybuf[j], buf[i+NX*j]);
+	 }
+      } else {			/* Restore */
+	 for(j=box->yfit[k]; dy*j<=dy*y1; j+=dy) {
+/* No 16 bit wraparound allowed */
+//	    if(delta < buf[i+NX*j]+BZERO) buf[i+NX*j] += delta;
+	    delta = box->zero[k] * ybuf[j] + 0.5;
+	    buf[i+NX*j] += delta;
+	 }
+      }
+   }
+
+   return(0);
+}
+
+/****************************************************************/
+/* Tuning parameters for fit_trail */
+#define MAXWGT 2.0	/* Maximum allowed weight */
+#define MINWGT 0.1	/* Minimum allowed weight */
+#define MAXDEV 0.5	/* Maximum ln deviation above first pass fit */
+#define MAXRMS 4.0	/* Minimum factor of RMS to be chopped */
+
+/* Fit one rectangle with a power law or exp: up=0/1 for down/up from y0 */
+STATIC int fit_trail(int nx, int ny, int NX, 
+		    DTYPE *data, MTYPE *mask, OBJBOX *box,
+		    int up, int bckgnd, int rms, int fitfunc)
+{
+   int i, j, k, err, nfit;
+   int xs, xe, y0, y1, y2, dy, yfit;
+   double slope, zero, zsum, wsum, trial;
+   char fooname[60];
+   FILE *fp=stdout;
+
+   if(ny > MAXSIZE) {
+      fprintf(stderr, "error: not enough buffer in fit_trail\n");
+      return(-1);
+   }
+
+   xs = box->sxfit;
+   xe = box->exfit;
+
+   if(up) {
+      y0 = (box->y0m + box->y1m + box->y0p + box->y0p + 2) / 4;
+/* Maybe a bit better?  Maybe not... */
+//      y0 = box->ey;
+      y1 = box->ey + FIT_EDGE;	/* Start of fit, budged away from the burn */
+      y2 = ny - 1;		/* End of fit */
+      dy = +1;
+      yfit = box->sy;		/* Start of validity, TBD later by col */
+   } else {
+      y0 = (box->y0m + box->y1m + box->y0p + box->y0p + 2) / 4;
+      y1 = 0;			/* Start of fit */
+      y2 = box->sy - FIT_EDGE;	/* End of fit, budged away from the burn */
+//      y2 = box->ey - FIT_EDGE;	/* End of fit, budged away from the burn */
+//      y2 = y0;	/* End of fit, budged away from the burn */
+      dy = -1;
+      yfit = box->ey;		/* Start of validity, TBD later by col */
+   }
+
+   if(VERBOSE & VERB_FIT) {
+      printf("Fit requested xs,xe,y0,y1,y2,dy,up,f= %d %d %d %d %d %d %d %d\n", 
+	     xs, xe, y0, y1, y2, dy, up, fitfunc);
+   }
+
+/* Make sure to alloc memory, even if the fit fails */
+   if(box->zero != NULL) free(box->zero);
+   if(box->xfit != NULL) free(box->xfit);
+   if(box->yfit != NULL) free(box->yfit);
+   box->zero = (double *)calloc(xe-xs+1, sizeof(double));
+   box->xfit = (int *)calloc(xe-xs+1, sizeof(int));
+   box->yfit = (int *)calloc(xe-xs+1, sizeof(int));
+/* Some defaults */
+   box->slope = 0.0;
+   box->func = fitfunc;
+   box->up = up;
+   box->y0 = y0;
+   for(i=0; i<xe-xs+1; i++) {
+      box->zero[i] = 0.0;
+      box->xfit[i] = i+xs;
+      box->yfit[i] = y0;
+   }
+
+   if(up && y1 >= ny-1) {
+      box->slope = -1.0;
+      box->nfit = xe - xs + 1;
+      box->fiterr = FIT_TOP_ERROR;
+      return(0);
+   }
+
+   if(VERBOSE & VERB_FITPROF) {
+      sprintf(fooname, "/tmp/foobt.%d", xs);
+      fp = fopen(fooname, "w");
+   }
+
+/* Accumulate points to fit */
+   nfit = y2 - y1 + 1;
+   for(j=y1; j<=y2; j++) {
+      ybuf[j] = Y_SCALE * dy * (j-y0);
+      zbuf[j]=0.0;
+      for(i=xs, k=0; i<=xe; i++) {
+//	 printf("%4d %4d %7d %7d\n", i, j, data[i+j*NX], mask[i+j*NX]);
+	 if(data[i+j*NX] != NODATA && mask[i+j*NX] == MASK_NONE) {
+//	 if(data[i+j*NX] != NODATA) {
+	    zbuf[j] += data[i+j*NX];
+	    k++;
+	 }
+      }
+//      printf("%4d %4d %7d\n", k, j, zbuf[j]);
+      zbuf[j] = zbuf[j] / MAX(1,k) - bckgnd;
+/* Linearize for fit... */
+      if(fitfunc == BURN_PWR) ybuf[j] = log(ybuf[j]);
+      wbuf[j] = 0;
+      if(zbuf[j] > 1) {
+	 wbuf[j] = MIN(MAXWGT, (zbuf[j] / rms));  /* Quadratic not good */
+	 wbuf[j] = MAX(MINWGT, wbuf[j]);
+	 zbuf[j] = log(zbuf[j]);
+      }
+   }
+/* First pass fit */
+   err = wlinearfit(nfit, ybuf+y1, zbuf+y1, wbuf+y1, &slope, &zero);
+   if(VERBOSE & VERB_FIT) {
+      printf("nfit= %d  err= %d  slope= %.3f  zero= %.3f\n", 
+	     nfit, err, slope, zero);
+   }
+   if(err) {
+      box->fiterr = FIT_ERROR;
+      return(-1);
+   }
+
+/* Trim away any really big positive deviations */
+   for(j=y1; j<=y2; j++) {
+      if(zbuf[j]-(ybuf[j]*slope+zero) > MAXDEV && 
+	 zbuf[j] > log(MAXRMS*rms)) wbuf[j] = 0;
+      if(VERBOSE & VERB_FITPROF) {
+	 fprintf(fp, "%9.4f %9.4f %9.4f %3d\n", ybuf[j], zbuf[j], wbuf[j], j);
+      }
+   }
+/* Second pass fit */
+   err = wlinearfit(nfit, ybuf+y1, zbuf+y1, wbuf+y1, &slope, &zero);
+   if(err) {
+      box->fiterr = FIT_ERROR;
+      return(-1);
+   }
+   box->fiterr = 0;		/* Whew, got a fit */
+
+   if(VERBOSE & VERB_FITPROF) {
+      printf("jt %d  %.3f %.3f\n", xs, zero, slope);
+      fclose(fp);
+   }
+
+/* FIXME: sanity check fits */
+   if(slope >= FIT_MAX_SLOPE || slope < FIT_MIN_SLOPE) {
+      box->fiterr = FIT_SLOPE_ERROR;
+      return(-1);
+   }
+
+/* Reconstruct the fit for comparison with the data col by col */
+   for(j=y1; j<=y2; j++) zbuf[j] = exp(ybuf[j]*slope);
+/* And tag on the extras to determine the end of the fit */
+   for(j=yfit; dy*j<dy*(up?y1:y2); j+=dy) {
+      if(fitfunc == BURN_EXP) {
+//	 zbuf[j] = exp(Y_SCALE*MAX(0,dy*(j-y0))*slope);
+	 zbuf[j] = exp(Y_SCALE*dy*(j-y0)*slope);
+      } else {
+	 if(dy*(j-y0) <= 0) zbuf[j] = exp(log(Y_SCALE*1.0)*slope);
+	 else zbuf[j] = exp(log(Y_SCALE*dy*(j-y0))*slope);
+      }
+   }
+
+/* Save the fit information */
+   box->slope = slope;
+   box->nfit = 0;
+/* Save individual scaled versions for each column */
+   for(i=xs; i<=xe; i++) {
+      zsum = wsum = 0.0;
+      for(j=y1; j<=y2; j++) {
+	 if(wbuf[j] > 0 && data[i+j*NX] != NODATA && 
+	    mask[i+j*NX] == MASK_NONE) {
+//	 if(wbuf[j] > 0 && data[i+j*NX] != NODATA) {
+//	    wsum += wbuf[j];
+//	    zsum += wbuf[j] * (data[i+j*NX] - bckgnd) / zbuf[j];
+	    wsum += zbuf[j];
+	    zsum += data[i+j*NX] - bckgnd;
+	 }
+      }
+      if(zsum < 0 || wsum <= 0) {
+	 zsum = 0.0;
+      } else {
+	 zsum = zsum / wsum;
+      }
+/* FIXME: what's a really good criterion for negligible fit? */
+/* 100 pixels up fit is zsum or ~zsum/e */
+      if(zsum > NEGLIGIBLE_TRAIL*rms) {
+
+/* Ascertain the starting point of where the fit is good */
+//      box->yfit[box->nfit] = yfit;	/* Vanilla, misses center */
+//      box->yfit[box->nfit] = y0;	/* Center, no adjust */
+	 for(j=yfit; dy*j<=dy*(up?y2:y1); j+=dy) {
+	    trial = zsum * zbuf[j];
+/* First time data - fit is closer to sky than is data */
+	    if(data[i+j*NX] > bckgnd + 0.5*trial) break;
+	 }
+	 box->yfit[box->nfit] = j;
+	 box->zero[box->nfit] = zsum;
+	 box->xfit[box->nfit] = i;
+	 box->nfit += 1;
+      }
+   }
+/* Update fit ranges */
+   box->sxfit = box->xfit[0];
+   box->exfit = box->xfit[box->nfit-1];
+
+   return(0);
+}
Index: /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfh/FHRealTime
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfh/FHRealTime	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfh/FHRealTime	(revision 23594)
@@ -0,0 +1,186 @@
+
+DRAFT - Real-Time extensions to libfh for "video" mode image clients.
+---------------------------------------------------------------------
+
+I. Definition of "real-time" display mode
+
+  A FITS viewer could be started in a special mode (e.g., with
+  a command line option) in which it is intended to display
+  only "current" image data.  The idea is to define an interface
+  that allow a FITS client to determine:
+
+  A. The number of extensions (if any) and the dimensions of the
+     image area(s), and whether or not the dimensions have changed.
+
+  B. If a file is current, whether or not it is still dynamically
+     changing (currently being written by DetCom, for example.)
+
+  C. If a single plane/image is partially written, which regions
+     are complete and which are yet to be read (e.g. from the camera.)
+
+  D. If the file has NAXIS>2, which plane in a cube is the most recent.
+
+  E. If a single plane/image is being built from an iterative
+     process such as on-the-ramp sampling, or being re-used
+     as in a circular buffer for guider images, which revision
+     of the data currently exists.
+
+  F. Whether a file is the most current or not, and which file
+     supercedes it.
+
+II. Methods
+
+  A. Extensions, file geometry - When a FITS client attempts to open
+     a new file, libfh takes care of waiting for the proper read-lock
+     to happen.  Once such a read lock can be obtained, the following
+     items are guaranteed to be populated and must not be changed
+     by detcom or whatever is providing the FITS file:
+
+     1. The number of extensions (NEXTEND in the primary header unit)
+     2. The number of dimensions (NAXIS for each image)
+     3. The dimensions themselves (NAXIS1, NAXIS2, NAXIS3, ...)
+     4. The pixel format (BITPIX)
+
+     . . . others?
+
+     Use of BITPIX and NAXIS* follows the FITS standard.
+
+     Use of extensions requires a little explanation.  If the
+     file has no extensions, a basic FITS image is assumed.
+     Otherwise, a set of FITS image extensions (other extension
+     types ignored??) are expected, exactly equal to NEXTEND
+     in the primary header.  The steps below may apply either
+     to a single basic FITS, or to an iteration through each
+     extension.
+
+     The above information is essentially available as soon as the
+     file exists.  This is ensured by the way a program generating
+     a FITS file using libfh uses file locks.
+
+  B. Next, a keyword RTACTIVE=T/F can be checked.  Its presence
+     during step (A) indicates whether or not the current FITS
+     buffer is going to support the real-time extensions that follow.
+     Initially, it will appear as T(rue), meaning that the FITS
+     file is actively being formed in real time.  Once the file will
+     no longer be changing RTACTIVE will change to F(alse).  The
+     internal methods in libfh for determining progress of a real-time
+     file are affected by this keyword in the following way:
+
+       RTACTIVE=T: Checks are made for zero-pixels plus a signature
+                   pixel within each image plane (if cubes) to determine
+                   current real-time progress of a frame, or a cube sequence.
+       RTACTIVE=F: Progress functions return 100% complete.
+       RTACTIVE not present: Real-time extensions should be disabled and
+                             progress functions also return 100% complete.
+
+  C. During the process of reading pixels from a camera, it may
+     be desirable to display the part of the image area which has
+     been read if the process takes several seconds.  For this case,
+     each extension (or the single FITS image) must end up getting
+     filled from the beginning or end of its image area buffer.
+     (If NOAO's model was followed strictly, and no flipping is
+     ever done on pixel data as it is read from the amplifier, only
+     the beginning-to-end case would be needed, but we have decided
+     to do some of our own flipping.)
+
+     By setting the RTACTIVE keyword, a creator of a FITS file is
+     indicating that it has initialized the pixel image areas with
+     marker-pixels at select locations.  It must set these if the
+     RTACTIVE keyword is used.  These locations are checked by libfh
+     to determine the progress of a FITS file being written.
+
+  D. In addition to determining the progress within a 2D image plane,
+     the marker pixels can be used to determine which plane(s) of
+     a cube have been written so far, by checking which planes are
+     still at 0%.  FITS providers which will be re-using a cube as a
+     circular buffer of frames (e.g., for guiding) must always maintain
+     an entire frame as unused (0%) by rewriting the marker pixels
+     in that buffer, before re-using the last remaining buffer with
+     marker pixels.  In this way, the most recent frame shall be determined
+     by looping through all of the planes and finding the first one
+     that is at 0%, and then backing up one (with a modulo.)  This finds
+     the most recent complete or partially complete frame.  To find the
+     most recently completed frame only, the same method is followed
+     except the search is made for the first frame that is <100% instead
+     of equal to 0%.  This plane will be the most recent one that an
+     image display should attempt to render at all times.
+
+  E. A keyword RTVERS will contain a version number (integer) which
+     increments each time a buffer is re-used.  If a client uses the
+     methods from (D) to determine that it is still on the correct
+     cube plane, (C) is used to compare the displayed-part with the
+     now-available-part.  However, the value of displayed-part must
+     be reset to 0 whenever the RTVERS number no longer matches.
+
+  F. Finding the "current" file.
+
+     For a science frame, creation of a totally new "current" file
+     may correspond to going to the next odometer number.  If a display
+     has been trained on viewing the current image stream from the
+     camera, it must switch to the new file when this happens.
+
+     For a guider, when a stream of images are not being saved, the
+     filename will typically be re-used (see (E), above)
+     However, since it greatly simplifies things
+     to constrain the size (NAXIS1, NAXIS2) of an image once it is
+     created, the guider could also use a jump to a new filename as
+     a signal that the image size may have changed, but otherwise be
+     able to rely on those and other parameters (BITPIX, etc.) not
+     changing.
+
+     The FITS client could accept control messages on stdin, telling
+     it to switch to a new FITS file location (and possibly do other
+     things, like turning on/off histogram equalization) through
+     the use of a basic command set.
+
+     From the session side, a link or symbolic link could be maintained
+     which always indicates the most "current" science and guide frame.
+     The full filename of the most recent frame could also be kept in a
+     fixed variable in the status server.
+
+     Joining the two previous pieces together would be a separate program
+     that monitors the filename in the status server and sends the command
+     to update to the FITS viewer.  For example:
+
+ssFitsMonitor /i/wircam/guide.fits | rtfits
+
+     Note that ssFitsMonitor would only be sending an update on every frame
+     if this happens to be a science frame done without cubes.  For other
+     cases where a cube is being built, rtfits would use libfh to poll
+     for the most recent cube plane.
+
+III. Keyword Examples
+
+RTACTIVE=                        T / Is file still being built?
+RTVERS  =                        3 / Number of real-time re-writes
+
+  ... everything else is determined from the marker pixels.
+
+IV. Libfh convenience functions
+
+  Real-time FITS files can be opened and accessed the usual way
+  with standard libfh calls.  But instead of accessing the RT*
+  keywords and marker pixels directly, it is recommended to use
+  the following new functions in libfh to determine the state of
+  "real time" FITS files:
+
+fh_bool fh_rt_present(HeaderUnit hu);
+fh_bool fh_rt_active(HeaderUnit hu);
+int     fh_rt_version(HeaderUnit hu);
+int     fh_rt_plane_completed(HeaderUnit hu); /* Returns 0..(NAXIS3-1) */
+int     fh_rt_plane_latest(HeaderUnit hu);    /* Returns 0..(NAXIS3-1) */
+int     fh_rt_plane_row_latest(HeaderUnit hu, int plane); /* Returns 0..(NAXIS2-1) */
+
+  It is not always necessary to call fh_rt_present() and fh_rt_active().
+
+  If fh_rt_present() would return false, then:
+
+     fh_rt_active() will also always return false,
+
+  and whenever fh_rt_active() would return false, then:
+
+     fh_rt_version() always returns some constant number,
+     fh_rt_plane_completed() returns NAXIS3-1, or 0 if no NAXIS3,
+     fh_rt_plane_latest() returns NAXIS3-1, or 0 if no NAXIS3,
+     fh_rt_plane_row_latest() returns NAXIS2-1 for any plane.
+
Index: /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfh/Index
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfh/Index	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfh/Index	(revision 23594)
@@ -0,0 +1,18 @@
+# Description:
+
+  package		FITS Handling Library
+  version		1
+  organization		Canada-France-Hawaii Telescope
+  email			daprog@cfht.hawaii.edu
+  year			2001
+
+# Contents:
+
+  Makefile		Use with GNU make to build libfh
+
+  fh.h			FITS Handling Routines for Standard FITS and MEF
+  fh.c			FITS Handling Routines (see fh.h for descriptions)
+  fh_rt.h		FITS RealTime routines
+  fh_rt.c		FITS RealTime routines
+  fh_validate.c		A routine to validate the completeness of header.
+  fh_registry.h		A registry of FITS keywords in use at CFHT.
Index: /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfh/Make.Common
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfh/Make.Common	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfh/Make.Common	(revision 23594)
@@ -0,0 +1,1 @@
+link ../Make.Common
Index: /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfh/Makefile
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfh/Makefile	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfh/Makefile	(revision 23594)
@@ -0,0 +1,25 @@
+# `Makefile' - Use with GNU make to build libfh
+#
+#   This file is part of version 1 of the FITS Handling Library.
+#   Read the `License' file for terms of use and distribution.
+#   Copyright 2001, Canada-France-Hawaii Telescope, daprog@cfht.hawaii.edu.
+#
+# ___This header automatically generated from `Index'. Do not edit it here!___
+# Makefile for libfh
+
+include ../Make.Common
+
+VERSION = 2.01
+CCWARN += $(WERROR)
+CCDEFS += -DHAVE_FH_VALIDATE
+SRCS = fh.c
+HDRS = fh.h fh_registry.h fh_registry.asm
+
+fh_registry.asm: fh_registry.h fh_registry_to_asm.sh
+	./fh_registry_to_asm.sh < $< > $@
+
+include ../Make.Common
+
+# Dependencies by Make.Common $Revision: 2.17 $
+
+$(OBJ)/fh.o: fh.c fh.h fh_validate.c
Index: /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfh/fh.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfh/fh.c	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfh/fh.c	(revision 23594)
@@ -0,0 +1,3192 @@
+/*                                             -*- c-file-style: "Ellemtel" -*-
+
+`fh.c' - FITS Handling Routines (see fh.h for descriptions)
+
+This file is part of version 1 of the FITS Handling Library.
+Read the `License' file for terms of use and distribution.
+Copyright 2001, Canada-France-Hawaii Telescope, daprog@cfht.hawaii.edu.
+
+___This header automatically generated from `Index'. Do not edit it here!___ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>	/* for read() and write() */
+#include <fcntl.h>	/* For file locking */
+#include <string.h>
+#include <ctype.h>
+#include <limits.h>
+#include <float.h>
+#include <errno.h>
+
+#include "fh.h"
+
+static const char rcs_id[] = "@(#) $Id: fh.c,v 1.8 2003/05/28 06:47:23 isani Exp isani $";
+
+/*
+ * -----------------------------
+ * Internal values used by libfh
+ * -----------------------------
+ */
+#define FH_BUFFER_SIZE (128*1024) /* Buffer for copying data between FITS files */
+#define IDX_AUTO_INCR 0.001	/* Auto-increment idx numbers if 0.0 used */
+
+#define FH_RESERVE \
+"COMMENT  Reserved space.  This line can be used to add a new FITS card.         "
+#define FH_RESERVE_LEN 80
+
+#define FH_XTENSION_UNKNOWN 0
+#define FH_XTENSION_IMAGE   1
+#define FH_XTENSION_TABLE   2
+
+#define MAX_SIG_FIGS 62 /* An 80-column FITS card cannot hold more than
+			 * this many significant figures, if a double
+			 * precision float value is printed with an E+
+			 * exponent that could have up to 3 digits too,
+			 * and a decimal place, and a leading - if the
+			 * number is negative.
+			 */
+
+/*
+ * ---------------------------------
+ * Internal structures used by libfh
+ * ---------------------------------
+ */
+typedef struct
+{
+   double idx;		/* Sorting number */
+   char card[FH_CARD_SIZE+1]; /* card[80] is always '\0' so library can use strspn() */
+} FitsCard;
+
+typedef FitsCard* FitsCardPtr;
+/*
+ * The `FitsCard' structure contains the 80 bytes, exactly as they
+ * will appear in the FITS file, along with an "idx" floating point
+ * sorting number (which is removed when writing a file.)
+ */
+
+/*
+ * NOTE: When adding members to this structure, please check
+ * the block of code in fh_file() that checks "if (extname)"
+ * because it needs to decide whether to keep each element of
+ * the structure from the PHU or the EHU.
+ */
+#define HU_MAGIC 0x4855 /* "HU" */
+typedef struct
+{
+   unsigned int magic;	/* Just there to help routines make sure they got a HeaderUnit */
+   FitsCardPtr* hdr;	/* Table of FITS cards */
+   int len;		/* # of cards (excl. END and 1 blank line which will be added) */
+   int pos;		/* Loop counter for fh_first()/fh_next() */
+   int size;		/* # of slots allocated so far (for realloc) */
+   int bitpix;		/* Cached copy of BITPIX keyword */
+   int image_bytes;	/* Cached copy of calculated size of image */
+   int image_bytes_left;/* Used for fh_read_image and fh_write_image */
+   int extensions;	/* Cached copy of NEXTEND keyword (if EXTEND=T) */
+   int xtension;	/* Cached copy of XTENSION (0=none 1=IMAGE 2=TABLE) */
+   double idx_highest;	/* Highest idx number used (for auto-incrementing) */
+   int set_all_units;	/* Should fh_set* functions apply to extensions automatically? */
+   HeaderUnit ehu;	/* Headers for first contained extension (if any) */
+   HeaderUnit next;	/* Headers for next extension (if _this_ is an extension) */
+   HeaderUnit phu;	/* Headers from the primary (if _this_ is an extension) */
+   struct flock fl;	/* File lock, if any. */
+   int fd;		/* -1 if no file associated with this header unit */
+   int fd_locked;	/* -1 if no file has been locked */
+   int fd_to_close;	/* Make sure fh_destroy closes fds opened by fh_file */
+   int fd_header_blocks; /* # of header blocks in the original file (for fh_rewrite) */
+   int file_size;	/* Total size of file for mmap use only */
+   int mmap_count;      /* Number of extensions currently mapped */
+   void* mmap_addr;     /* -1 if file has not been memory-mapped */
+   int reserve;		/* Number of blank cards to reserve (for fh_write) */
+   int reserve_found;	/* Number of reserve cards found in existing header */
+   off_t off_hdrs;	/* Offset in file to this set of cards */
+   off_t off_data;	/* Offset in file to data (or extensions) after the cards */
+   int err_memory;	/* Used by fh_set*() to count memory errors */
+   int err_invalid;	/* Used by fh_set*() to count invalid argument errors */
+   int counting_cards;	/* No, we're not going to Vegas.  When this flag is set, the
+			 * library is in a special mode where fh_[re]write() does nothing
+			 * to a file, but instead prints the number of cards your program
+			 * is going to need to stdout.  This value should be captured and
+			 * summed together with other programs to calculate the value
+			 * to be passed to fh_reserve() by upstream programs which
+			 * actually create the FITS file.
+			 */
+} HeaderUnitStruct;
+
+/*
+ * The following cast is needed because the outside world doesn't
+ * see the internals of a HeaderUnitStruct.  Instead, they pass a
+ * (void*) pointer (HeaderUnit) which must be cast back to the real type.
+ */
+#define FH_HU(hu) (((hu)&&((HeaderUnitStruct*)(hu))->magic==HU_MAGIC)?\
+		(HeaderUnitStruct*)(hu):0)
+
+/*
+ * This table is for converting characters into legal
+ * characters for fits card NAMES.  It is equivalent to:
+ * 
+ * if (isalpha(c) || isdigit(c) || *name=='-' || *name=='_')
+ *   return = toupper(c);
+ * else
+ *   return '_';
+ */
+static char name_chr[256] =
+" _______________"
+"________________"
+" ____________-__"
+"0123456789______"
+"_ABCDEFGHIJKLMNO"
+"PQRSTUVWXYZ_____"
+"_ABCDEFGHIJKLMNO"
+"PQRSTUVWXYZ_____"
+"________________"
+"________________"
+"________________"
+"________________"
+"________________"
+"________________"
+"________________"
+"________________";
+
+#define NAME_CHR(c) (name_chr[(unsigned int)((unsigned char)(c))])
+
+/*
+ * The following is the return type of the Keyword name matching function.
+ * It is used for several thins internally in this library.  The highest
+ * valid return code is the one returned.
+ */
+typedef enum
+{
+   MATCH_FAILED = -1, /* Names do not match */
+   MATCH_SUBSTR = 0,  /* Name1 begins with all the characters in name2 */
+   MATCH_KEYWORD = 1, /* Names are the same for first 8 characters */
+   MATCH_FULL = 2     /* Names are the same (up to 80 chars compared) */
+} match_result;
+
+/*
+ * This library requires a SYSV style sprintf, which returns the number
+ * of bytes added to the string, just like printf and fprintf.
+ */
+#ifdef SUNOS
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+static int sysv_sprintf(char* s, const char* fmt, ...)
+{
+   va_list args;
+#ifdef __STDC__
+   va_start(args, fmt);
+#else
+   va_start(args);
+#endif
+   vsprintf(s, fmt, args);
+   return strlen(s);
+}
+#else
+#define sysv_sprintf sprintf
+#endif
+
+/*
+ * ----------------------------------------------
+ * Internal functions for implementation of libfh
+ * ----------------------------------------------
+ * (These are found at the end of this file.)
+ */
+
+/* Functions that do file operations WITH RETRY: */
+static int write_file(int fd, const void* buf, int len);
+static int read_file(int fd, const void* buf, int len);
+static int seek_file(int fd, off_t off); /* Performs lseek(SEEK_SET) */
+static int fh_lock_file(HeaderUnit hu, int fd, fh_mode mode);
+static int fh_unlock_file(HeaderUnit hu);
+
+static const char* padding_block(int type); /* Buffer of \0's or ' ' */
+static fh_result fix_characters(char* s, int repair); /* Fix bad chars in card */
+static match_result fh_cmp(const char* name1, const char* name2); /* Compares keywords */
+static int fh_compare(const void* a, const void* b); /* Compares `idx' */
+static void fh_sort(HeaderUnit hu); /* Uses fh_compare to sort HeaderUnit */
+
+/* Internal functions used to get and build cards */
+static FitsCardPtr get_hdr(HeaderUnitStruct* list, const char* name, double idx);
+static const char* get_value(HeaderUnitStruct* list, const char* name);
+static double auto_idx(const char* name);
+static char* get_card(HeaderUnitStruct* list, double idx, const char* name,
+		      double idxmatch);
+static void add_comment(char* s, int col, const char* comment);
+static void show_card_count(HeaderUnitStruct* hu) { printf("%d\n", FH_HU(hu)->len); }
+
+/*
+ * ----------------------
+ * Main library functions
+ * ----------------------
+ */
+#ifdef HAVE_FH_VALIDATE
+#include "fh_validate.c"
+#endif
+
+/*
+ * This is just so rcs_id doesn't turn up "unused".
+ * And who knows, maybe someone wants their program to print
+ * this information in debug mode.
+ */
+const char* fh_rcs_version(void) { return rcs_id; }
+
+/*
+ * This gets called any time a new header is set to force things like
+ * image_bytes and extensions to be re-calculated in case a relevant
+ * keyword got changed.  It's also used in fh_create().
+ */
+static void
+invalidate_hu_cache(HeaderUnitStruct* list)
+{
+   list->bitpix = list->image_bytes = list->extensions = list->xtension = -1;
+}
+
+/*
+ * ---------------------------
+ * Error/warning logging stuff
+ * ---------------------------
+ */
+
+/*
+ * Error logger routines take a single string constant parameter.
+ * There is one routine for errors and another for warnings.
+ * The library does not generate any other kinds of messages.
+ */
+
+static char err_buf[1024]; /* Buffer for building error strings */
+
+static void
+dfl_log_error(const char* s) { fprintf(stderr, "error: libfh: %s\n", s); }
+
+static void
+dfl_log_perror(const char* s) { perror(s); }
+
+static void
+dfl_log_warning(const char* s) { fprintf(stderr, "warning: libfh: %s\n", s); }
+/*
+ * Default message handlers print to stderr with the prefix "error:" or "warning:"
+ */
+
+static void
+null_log(const char* s) { if (s) ; }
+/*
+ * It's also possible to pass a NULL to fh_log_xxx() and rely only on the
+ * error returns from the functions themselves.
+ */
+
+static fh_logger_t log_error = dfl_log_error;
+static fh_logger_t log_perror = dfl_log_perror;
+static fh_logger_t log_warning = dfl_log_warning;
+
+/*
+ * Functions to install your own warning/error handlers:
+ */
+void
+fh_log_error(fh_logger_t newlogger)
+{
+   if (newlogger == 0) log_error = null_log;
+   else log_error = newlogger;
+}
+
+void
+fh_log_perror(fh_logger_t newlogger)
+{
+   if (newlogger == 0) log_perror = null_log;
+   else log_perror = newlogger;
+}
+
+void
+fh_log_warning(fh_logger_t newlogger)
+{
+   if (newlogger == 0) log_warning = null_log;
+   else log_warning = newlogger;
+}
+
+HeaderUnit
+fh_create()
+{
+   HeaderUnitStruct* rtn; 
+
+   if (!(rtn = (HeaderUnitStruct*)malloc(sizeof(HeaderUnitStruct))) ||
+       !(memset(rtn, 0, sizeof(HeaderUnitStruct))) ||
+       !(rtn->hdr = (FitsCardPtr*)malloc(sizeof(FitsCardPtr) * 200)))
+   {
+      if (rtn) free(rtn);
+      log_error("out of memory in fh_create()");
+      return 0;
+   }
+   rtn->len = 0;
+   rtn->size = 200;
+   rtn->idx_highest = 1000.000; /* User keywords start a 1000.001 */
+   rtn->fd = -1;
+   rtn->fd_locked = -1; /* Set if file descriptor need to be unlocked */
+   rtn->fd_to_close = -1; /* Set if fh_destroy() will close fd from fh_file */
+   rtn->fd_header_blocks = 0; /* Not valid unless fd >= 0 */
+   rtn->file_size = 0; /* Total size of file from stat() */
+   rtn->mmap_count = 0; /* Not memory mapped initially */
+   rtn->mmap_addr = (void*)-1;
+   rtn->reserve = 0;
+   rtn->reserve_found = 0;
+   rtn->ehu = 0;
+   rtn->next = 0;
+   rtn->off_hdrs = 0;
+   rtn->off_data = 0;
+   rtn->fl.l_type = F_UNLCK;
+   rtn->err_memory = 0;
+   rtn->err_invalid = 0;
+   rtn->counting_cards = 0;
+   invalidate_hu_cache(rtn);
+   rtn->magic = HU_MAGIC; /* Just used as a sanity check */
+   return (HeaderUnit)rtn;
+}
+
+/*
+ * Future fh_set_*() calls apply to any/all EHU's that were
+ * found in the same file.
+ */
+fh_result
+fh_set_all_units(HeaderUnit hu)
+{
+   HeaderUnitStruct* list = FH_HU(hu);
+   off_t off;
+   int ext;
+
+   if (!list)
+   {
+      log_error("Invalid HeaderUnit passed to fh_set_all_units()");
+      return FH_INVALID;
+   }
+
+   if (list->fd == -1)
+   {
+      log_error("fh_set_all_units only applies to HeaderUnits "
+		"read from a file or file descriptor");
+      return FH_INVALID;
+   }
+
+   /*
+    * Save current file position, make sure all extensions are
+    * cached, and then restore the current file position.  If this
+    * is skipped, then the other fh_set() functions would have the
+    * unexpected side-effect of moving the file position around
+    * they happen to cause the first access to an EHU.
+    */
+   off = lseek(list->fd, 0, SEEK_CUR);
+   for (ext = 1; ext <= fh_extensions(list); ext++)
+   {
+      if (fh_ehu(hu, ext) == 0)
+	 return FH_NOT_FOUND;
+   }
+   seek_file(list->fd, off);
+
+   list->set_all_units = 1;
+   return FH_SUCCESS;
+}
+
+fh_result
+fh_destroy(HeaderUnit hu)
+{
+   fh_result rtn = FH_SUCCESS;
+   HeaderUnitStruct* list = FH_HU(hu);
+
+   if (!list)
+   {
+      log_error("Invalid HeaderUnit passed to fh_destroy()");
+      return FH_INVALID;
+   }
+
+   if (fh_unlock_file(hu) == -1)
+      rtn = FH_IN_ERRNO;
+
+   if (list->fd_to_close != -1 &&
+       close(list->fd_to_close) != 0)
+   {
+      log_perror("close for fh_file() failed");
+      rtn = FH_IN_ERRNO;
+   }
+
+   while (list->len)
+      free(list->hdr[--list->len]);
+   free(list->hdr);
+
+   if (list->ehu) fh_destroy(list->ehu);
+   if (list->next) fh_destroy(list->next);
+
+   free(list);
+   return rtn;
+}
+
+fh_result
+fh_count_cards(HeaderUnit hu)
+{
+   HeaderUnitStruct* list = FH_HU(hu);
+
+   if (!list)
+   {
+      log_error("Invalid HeaderUnit passed to fh_count_cards()");
+      return FH_INVALID;
+   }
+   list->counting_cards++;
+   return FH_SUCCESS;
+}
+
+fh_result
+fh_file(HeaderUnit hu, const char* filespec, fh_mode mode)
+{
+   HeaderUnitStruct* list = FH_HU(hu);
+   char* extname = 0;
+   char* filename = 0;
+   struct stat st_buf;
+   int open_mode = 0, fd;
+   fh_result rtn = FH_SUCCESS;
+
+   if (!list)
+   {
+      log_error("Invalid HeaderUnit passed to fh_file()");
+      return FH_INVALID;
+   }
+
+   if (list->len)
+   {
+      log_error("fh_file() requires an empty HeaderUnit");
+      return FH_BAD_VALUE;
+   }
+
+   if (filespec && !strcmp(filespec, FH_COUNT_CARDS))
+   {
+      fh_count_cards(hu);
+      return FH_SUCCESS;
+   }
+
+   switch (mode)
+   {
+      case FH_FILE_RDONLY:
+      case FH_FILE_RDONLY_NOLOCK: open_mode = O_RDONLY; break;
+      case FH_FILE_RDWR:
+      case FH_FILE_RDWR_NOLOCK: open_mode = O_RDWR; break;
+      default:
+	 log_error("Invalid `mode' argument passed to fh_file()");
+	 return FH_INVALID;
+   }
+
+   /*
+    * If no filename, or if the filespec is "-", try to read from stdin.
+    */
+   if (!filespec || !strcmp(filespec, "-"))
+   {
+      if (isatty(STDIN_FILENO))
+      {
+	 log_error("cannot read from a terminal");
+	 return FH_IS_TTY;
+      }
+      if ((rtn = fh_read(hu, STDIN_FILENO, FH_AUTO)) != FH_SUCCESS)
+      {
+	 if (rtn != FH_END_OF_FILE)
+	    log_error("failed to read FITS header from stdin");
+	 return rtn;
+      }
+      return FH_SUCCESS; /* Successfully read header from stdin. */
+   }
+
+   /*
+    * Next test is to see if filespec, exactly as given, exists
+    * as a file (even including '[' ']' or other characters.)
+    */
+   if (stat(filespec, &st_buf) == 0)
+   {
+      if (S_ISDIR(st_buf.st_mode))
+      {
+	 sprintf(err_buf, "`%.900s' is a directory", filespec);
+	 log_error(err_buf);
+	 return FH_IS_DIR;
+      }
+      filename = strdup(filespec);
+   }
+   else
+   {
+      char* bracket_open = 0;
+      char* bracket_close = 0;
+
+      if (!(filename = (char*)malloc(strlen(filespec)*2 + 32)))
+      {
+	 log_error("out of memory in fh_file()");
+	 return FH_NO_MEMORY;
+      }
+      strcpy(filename, filespec);
+
+      /*
+       * Extract "[extname]" if present in filespec.
+       */
+      if ((bracket_open = strchr(filename, '[')) != 0 &&
+	  (bracket_close = strchr(bracket_open, ']')) != 0)
+      {
+	 *bracket_open++ = *bracket_close++ = '\0';
+	 extname = strdup(bracket_open);
+	 strcat(filename, bracket_close);
+      }
+
+      /*
+       * Make sure the filename contains ".fits" for the first test.
+       */
+      if (strlen(filename) <= 5 ||
+	  strcmp(filename + strlen(filename) - 5, ".fits"))
+	 strcat(filename, ".fits");
+
+      /*
+       * Now strip off the ".fits" and try that next.
+       */
+      if (stat(filename, &st_buf) != 0)
+      {
+	 filename[strlen(filename) - 5] = '\0';
+	 if (stat(filename, &st_buf) != 0)
+	 {
+	    log_perror(filespec);
+	    return FH_IN_ERRNO;
+	 }
+      }
+
+      if (S_ISDIR(st_buf.st_mode))
+      {
+	 const char* basename;
+
+	 if (!extname)
+	 {
+	    sprintf(err_buf, "`%.900s' is a directory", filespec);
+	    log_error(err_buf);
+	    return FH_IS_DIR;
+	 }
+
+	 /*
+	  * Get the part of filename without the path elements.
+	  */
+	 basename = strrchr(filename, '/');
+	 if (basename) basename++;
+	 else basename = filename;
+	 strcpy(filename + strlen(filename) + 1, basename);
+	 filename[strlen(filename)] = '/';
+
+	 if (!strncmp(extname, "chip", 4)) strcat(filename, extname + 4);
+	 else if (!strncmp(extname, "amp", 3)) strcat(filename, extname + 3);
+	 else if (!strncmp(extname, "im", 2)) strcat(filename, extname + 2);
+	 else strcat(filename, extname);
+	 strcat(filename, ".fits");
+	 free(extname); extname = 0;
+      }
+   }
+
+   /*
+    * Now try to open filename.
+    */
+   if ((fd = open(filename, open_mode, 0)) == -1)
+   {
+      log_perror(filename);
+      free(filename);
+      if (extname) free(extname);
+      return FH_IN_ERRNO;
+   }
+   free(filename);
+   list->fd_to_close = fd;
+   list->file_size = st_buf.st_size;
+
+   if (mode == FH_FILE_RDONLY || mode == FH_FILE_RDWR)
+   {
+      if (fh_lock_file(hu, fd, mode) == -1)
+	 log_perror("failed to lock file");
+   }
+
+   /*
+    * Read the (first) header unit from the file.
+    */
+   if ((rtn = fh_read(hu, fd, FH_AUTO)) != FH_SUCCESS)
+   {
+      if (extname) free(extname);
+      return rtn;
+   }
+
+   if (mode == FH_FILE_RDONLY)
+   {
+      if (fh_extensions(hu))
+      {
+	 /* === Read in the extension headers now, or just leave the file locked?
+	  * === For now, I'm opting to leave the whole file locked.
+	  */
+      }
+      else
+      {
+	 if (fh_unlock_file(hu) == -1)
+	    log_perror("failed to unlock file");
+      }
+   }
+
+   /*
+    * If an extension was given, try to locate that extension within
+    * the MEF file.  What happens here is a little gross.
+    */
+   if (extname)
+   {
+      HeaderUnit ehu;
+      fh_bool inherit;
+
+      if ((ehu = fh_ehu_by_extname(hu, extname)) == 0)
+      {
+	 sprintf(err_buf, "extension `%.900s' not found", extname);
+	 log_error(err_buf);
+	 free(extname);
+	 return FH_NOT_FOUND;
+      }
+      free(extname);
+      /*
+       * Extension was found, but the caller expects to find the keywords
+       * in `hu' which they passed in, not in `ehu'.  So the next step is
+       * either to replace or add to the cards already in `hu' to make it
+       * look like the extension.  Replace or add depends on INHERIT:
+       */
+      if (fh_get_bool(ehu, "INHERIT", &inherit) != FH_SUCCESS ||
+	  inherit == FH_FALSE)
+      {
+	 /*
+	  * Do not inherit keywords from the parent.
+	  */
+	 while (list->len)
+	    free(list->hdr[--list->len]);
+      }
+      /*
+       * In any case, do not inherit EXTEND and NEXTEND keywords.
+       */
+      fh_remove(hu, "EXTEND");
+      fh_remove(hu, "NEXTEND");
+      invalidate_hu_cache(list);
+      /*
+       * Change `hu' into the EHU by copying the following:
+       */
+      list->off_hdrs = FH_HU(ehu)->off_hdrs;
+      list->off_data = FH_HU(ehu)->off_data;
+      list->fd_header_blocks = FH_HU(ehu)->fd_header_blocks;
+      /*
+       * Now merge all the cards from the EHU into `hu'.
+       */
+      fh_merge(hu, ehu);
+      fh_destroy(list->ehu);
+      list->ehu = 0;
+   }
+
+   return FH_SUCCESS;
+}
+
+int
+fh_file_desc(HeaderUnit hu)
+{
+   return FH_HU(hu)->fd;
+}
+
+const char*
+fh_next(HeaderUnit hu)
+{
+   HeaderUnitStruct* list = FH_HU(hu);
+
+   if (!list || !list->len) return 0;
+   if (list->pos < list->len)
+      return list->hdr[list->pos++]->card;
+   return 0;
+}
+
+const char*
+fh_first(HeaderUnit hu)
+{
+   HeaderUnitStruct* list = FH_HU(hu);
+
+   if (!list) return 0;
+   list->pos = 0;
+   return fh_next(hu);
+}
+
+double
+fh_idx(HeaderUnit hu)
+{
+   HeaderUnitStruct* list = FH_HU(hu);
+
+   if (!list || !list->len) return 0;
+   if (list->pos > list->len || list->pos < 1) return 0;
+   return list->hdr[list->pos-1]->idx;
+}
+
+void
+fh_set_str(HeaderUnit hu, double idx,
+	   const char* name, const char* value, const char* comment)
+{
+   HeaderUnitStruct* list = FH_HU(hu);
+   char* s;
+   int col = FH_NAME_SIZE;
+
+   if (!list || !name || !value)
+   {
+      log_error("Invalid argument passed to fh_set_str()");
+      if (list) list->err_invalid++;
+      return;
+   }
+
+   /*
+    * First set this value for all extensions too, if applicable.
+    */
+   if (list->set_all_units)
+   {
+      int ext;
+
+      for (ext = 1; ext <= fh_extensions(list); ext++)
+	 fh_set_str(fh_ehu(hu, ext), idx, name, value, comment);
+   }
+
+   s = get_card(list, idx, name, 0); /* Replace any other with same name */
+   if (!s) return; /* malloc problem; will be caught later by fh_rewrite() */
+   s[col++] = '=';
+   s[col++] = ' ';
+   s[col++] = '\'';
+   while (col < 78 && *value)
+   {
+      if (*value < ' ' || *value >= 127)
+      {
+	 sprintf(err_buf, "illegal character %x in string FITS card", *value++);
+	 log_warning(err_buf);
+	 s[col++] = '_';
+      }
+      else if (*value == '\'')
+      {
+	 s[col++] = '\'';
+	 s[col++] = '\'';
+	 value++;
+      }
+      else
+      {
+	 s[col++] = *value++;
+      }
+   }
+   /* col could be as high as 79 now, if the last char was a '' */
+
+   while (col < 19)
+      s[col++] = ' '; /* Old FITS standard wants this :-/ */
+
+   s[col++] = '\'';
+
+   add_comment(s, col, comment);
+}
+
+void
+fh_set_com(HeaderUnit hu, double idx,
+	   const char* name, const char* comment)
+{
+   HeaderUnitStruct* list = FH_HU(hu);
+   int col = FH_NAME_SIZE;
+   char* s;
+
+   if (!list || !name || !comment)
+   {
+      log_error("invalid argument to fh_set_com()");
+      if (list) list->err_invalid++;
+      return;
+   }
+
+   /*
+    * First set this value for all extensions too, if applicable.
+    */
+   if (list->set_all_units)
+   {
+      int ext;
+
+      for (ext = 1; ext <= fh_extensions(list); ext++)
+	 fh_set_com(fh_ehu(hu, ext), idx, name, comment);
+   }
+
+   s = get_card(list, idx, name, idx); /* Only _replace_ if idx matches */
+   if (!s) return; /* malloc problem; will be caught later by fh_rewrite() */
+   while (col < FH_CARD_SIZE && *comment)
+   {
+      if (*comment)
+      {
+	 if (*comment < ' ' || *comment >= 127)
+	 {
+	    sprintf(err_buf, "illegal character %x in comment FITS card", *comment++);
+	    log_warning(err_buf);
+	    s[col++] = '_';
+	 }
+	 else
+	 {
+	    s[col++] = *comment++;
+	 }
+      }
+      else
+	 s[col++] = ' ';
+   }
+}
+
+void
+fh_set_int(HeaderUnit hu, double idx,
+	   const char* name, int value, const char* comment)
+{
+   HeaderUnitStruct* list = FH_HU(hu);
+   int col = FH_NAME_SIZE;
+   char* s;
+
+   if (!list || !name)
+   {
+      log_error("invalid argument to fh_set_int()");
+      if (list) list->err_invalid++;
+      return;
+   }
+
+   invalidate_hu_cache(list); /* In case this changed BITPIX or NAXIS */
+
+   /*
+    * First set this value for all extensions too, if applicable.
+    */
+   if (list->set_all_units)
+   {
+      int ext;
+
+      for (ext = 1; ext <= fh_extensions(list); ext++)
+	 fh_set_int(fh_ehu(hu, ext), idx, name, value, comment);
+   }
+
+   s = get_card(list, idx, name, 0); /* Replace any other with same name */
+   if (!s) return; /* malloc problem; will be caught later by fh_rewrite() */
+   s[col++] = '=';
+   s[col++] = ' ';
+
+   col += sysv_sprintf(s + col, "%20d", value);
+   s[col] = ' '; /* Remove the \0 that sprintf added (add_comment overwrites it too) */
+
+   add_comment(s, col, comment);
+}
+
+void
+fh_set_flt(HeaderUnit hu, double idx,
+	   const char* name, double value,
+	   int significant_digits, const char* comment)
+{
+   HeaderUnitStruct* list = FH_HU(hu);
+   int col = FH_NAME_SIZE;
+   char* s;
+
+   if (!list || !name)
+   {
+      log_error("invalid argument to fh_set_flt()");
+      if (list) list->err_invalid++;
+      return;
+   }
+
+   /*
+    * Sanity-check the significant_digits parameter and silently
+    * limit it to the size of the FITS card to prevent buffer
+    * overruns.  This is probably somewhat C-library dependent,
+    * and certainly dependent on the range of exponents that can
+    * occur in the floating point value being printed.  It assumes
+    * the optional "E+<exponent>" which may get printed will be at
+    * most 3 digits.  In that case, 62 significant digits and the
+    * space (or minus sign) in front of the number plus the decimal
+    * point will result in using all but the last column in the 80
+    * character FITS card.
+    */
+   if (significant_digits < 1 || significant_digits > MAX_SIG_FIGS)
+      significant_digits = MAX_SIG_FIGS;
+
+   /*
+    * First set this value for all extensions too, if applicable.
+    */
+   if (list->set_all_units)
+   {
+      int ext;
+
+      for (ext = 1; ext <= fh_extensions(list); ext++)
+	 fh_set_flt(fh_ehu(hu, ext), idx, name, value, significant_digits, comment);
+   }
+
+   s = get_card(list, idx, name, 0); /* Replace any other with same name */
+   if (!s) return; /* malloc problem; will be caught later by fh_rewrite() */
+   s[col++] = '=';
+   s[col++] = ' ';
+   col += sysv_sprintf(s + col, "%# 20.*G", significant_digits, value);
+   s[col] = ' '; /* Remove the \0 that sprintf added for us */
+
+   add_comment(s, col, comment);
+}
+
+void
+fh_set_pfl(HeaderUnit hu, double idx,
+	   const char* name, double value,
+	   int decimal_places, const char* comment)
+{
+   HeaderUnitStruct* list = FH_HU(hu);
+   int col = FH_NAME_SIZE;
+   char* s;
+   double i, hival = 0;
+
+   if (!list || !name)
+   {
+      log_error("invalid argument to fh_set_flt()");
+      if (list) list->err_invalid++;
+      return;
+   }
+
+   /*
+    * If the value is too big (or too negative) to be expressed
+    * without exponent with the number of columns that a FITS card
+    * allows, fall back to the other way of formatting floats.
+    * Also do this if decimal_places is more than MAX_SIG_FIGS.
+    * The alternate float-formatting routine fh_set_flt() will be
+    * used at the limit of MAX_SIG_FIGS and may automatically
+    * switch to E-notation, even if fh_set_pfl() was used.
+    * This is necessary to prevent buffer overflows and potential
+    * memory corruption when very large magnitude numbers get
+    * passed to fh_set_pfl().
+    *
+    * For performance reasons, the first few cases are pre-calculated
+    * in a switch statement.  For more than 9 decimal places, the
+    * CPU will have to do a bunch of divide by 10's for each value
+    * passed to fh_set_pfl() to figure out if it is going to fit or
+    * not.  Note that we don't want to suck in a dependency on libm
+    * and use log10().  We also didn't want to just switch to snprintf()
+    * because we were not sure if all the systems we might want to use
+    * libfh on all have that function.  (Plus, snprintf would probably
+    * corrupt and truncate the number.  This solution does not.)
+    */
+   switch (decimal_places)
+   {
+      case 0: hival = 1.0E66; break;
+      case 1: hival = 1.0E65; break;
+      case 2: hival = 1.0E64; break;
+      case 3: hival = 1.0E63; break;
+      case 4: hival = 1.0E62; break;
+      case 5: hival = 1.0E61; break;
+      case 6: hival = 1.0E60; break;
+      case 7: hival = 1.0E59; break;
+      case 8: hival = 1.0E58; break;
+      case 9: hival = 1.0E57; break;
+      default:
+      {
+	 if (decimal_places < MAX_SIG_FIGS)
+	 {
+	    hival = 1.0E56;
+	    for (i = 10; i < decimal_places; i++)
+	       hival /= 10.;
+	 }
+      }
+   }
+   if (value >= hival || value <= -hival)
+   {
+      fh_set_flt(hu, idx, name, value, MAX_SIG_FIGS, comment);
+      return;
+   }
+
+   /*
+    * First set this value for all extensions too, if applicable.
+    */
+   if (list->set_all_units)
+   {
+      int ext;
+
+      for (ext = 1; ext <= fh_extensions(list); ext++)
+	 fh_set_pfl(fh_ehu(hu, ext), idx, name, value, decimal_places, comment);
+   }
+
+   s = get_card(list, idx, name, 0); /* Replace any other with same name */
+   if (!s) return; /* malloc problem; will be caught later by fh_rewrite() */
+   s[col++] = '=';
+   s[col++] = ' ';
+   col += sysv_sprintf(s + col, "%# 20.*f", decimal_places, value);
+   s[col] = ' '; /* Remove the \0 that sprintf added for us */
+
+   add_comment(s, col, comment);
+}
+
+void
+fh_set_val(HeaderUnit hu, double idx,
+	   const char* name, const char* value, const char* comment)
+{
+   HeaderUnitStruct* list = FH_HU(hu);
+   int col = FH_NAME_SIZE;
+   int len = strlen(value);
+   char* s;
+   char* endptr;
+
+   if (!list || !name || !value)
+   {
+      log_error("invalid argument to fh_set_val()");
+      if (list) list->err_invalid++;
+      return;
+   }
+
+   /*
+    * If the value is not boolean or numeric, save it as a string.
+    */
+   strtod(value, &endptr);
+   if (strcmp(value, "T") &&
+       strcmp(value, "F") &&
+       (!endptr || *endptr!='\0'))
+   {
+      /*
+       * Strip quotes if needed.
+       */
+      if (*value == '\'' && strlen(value) > 1 &&
+	  value[strlen(value) - 1] == '\'')
+      {
+	 char* newval = strdup(value + 1);
+
+	 newval[strlen(newval) - 1] = '\0';
+	 fh_set_str(hu, idx, name, newval, comment);
+	 return;
+      }
+      fh_set_str(hu, idx, name, value, comment);
+      return;
+   }
+
+   /*
+    * First set this value for all extensions too, if applicable.
+    */
+   if (list->set_all_units)
+   {
+      int ext;
+
+      for (ext = 1; ext <= fh_extensions(list); ext++)
+	 fh_set_val(fh_ehu(hu, ext), idx, name, value, comment);
+   }
+
+   if (len > 70)
+   {
+      sprintf(err_buf, "truncating value for [%.8s]", name);
+      log_warning(err_buf);
+   }
+
+   s = get_card(list, idx, name, 0); /* Replace any other with same name */
+   if (!s) return; /* malloc problem; will be caught later by fh_rewrite() */
+   s[col++] = '=';
+   s[col++] = ' ';
+   while (len < 20) { s[col++] = ' '; len++; } /* Right justify */
+   col += sysv_sprintf(s + col, "%.70s", value);
+   if (col < FH_CARD_SIZE)
+   {
+      s[col] = ' '; /* Fix the \0 that sprintf added for us */
+      add_comment(s, col, comment);
+   }
+}
+
+void
+fh_set_bool(HeaderUnit hu, double idx,
+	    const char* name, fh_bool value, const char* comment)
+{
+   HeaderUnitStruct* list = FH_HU(hu);
+   int col = FH_NAME_SIZE;
+   char* s;
+
+   if (!list || !name)
+   {
+      log_error("invalid argument to fh_set_bool()");
+      if (list) list->err_invalid++;
+      return;
+   }
+
+   /*
+    * First set this value for all extensions too, if applicable.
+    */
+   if (list->set_all_units)
+   {
+      int ext;
+
+      for (ext = 1; ext <= fh_extensions(list); ext++)
+	 fh_set_bool(fh_ehu(hu, ext), idx, name, value, comment);
+   }
+
+   s = get_card(list, idx, name, 0); /* Replace any other with same name */
+   if (!s) return; /* malloc problem; will be caught later by fh_rewrite() */
+   s[col++] = '=';
+   while (col < 29) s[col++] = ' ';
+   s[col++] = value?'T':'F';
+
+   add_comment(s, col, comment);
+}
+
+void
+fh_set_card(HeaderUnit hu, double idx, const char* card)
+{
+   HeaderUnitStruct* list = FH_HU(hu);
+   const char* name = 0;
+   char* s;
+   int len;
+
+   if (!list || !card)
+   {
+      log_error("invalid argument to fh_set_card()");
+      if (list) list->err_invalid++;
+      return;
+   }
+
+   len = strlen(card);
+   if (len > FH_CARD_SIZE)
+   {
+      log_error("card too long for fh_set_card()");
+      if (list) list->err_invalid++;
+      return;
+   }
+
+   /*
+    * First set this value for all extensions too, if applicable.
+    */
+   if (list->set_all_units)
+   {
+      int ext;
+
+      for (ext = 1; ext <= fh_extensions(list); ext++)
+	 fh_set_card(fh_ehu(hu, ext), idx, card);
+   }
+
+   if (len > FH_NAME_SIZE && card[FH_NAME_SIZE] == '=') name = card;
+   s = get_card(list, idx, name, 0); /* Replace any other with same name */
+   if (!s) return; /* malloc problem; will be caught later by fh_rewrite() */
+   memcpy(s, card, len);
+}
+
+fh_result
+fh_get_bool(HeaderUnit hu, const char* name, fh_bool* value)
+{
+   HeaderUnitStruct* list = FH_HU(hu);
+   const char* p;
+
+   if (!list || !name || !value) return FH_INVALID; /* Bad arguments */
+   if (!(p = get_value(list, name))) return FH_NOT_FOUND;
+   if (p[0] == 'F' && p[1] == ' ') { *value=FH_FALSE; return FH_SUCCESS; }
+   if (p[0] == 'T' && p[1] == ' ') { *value=FH_TRUE; return FH_SUCCESS; }
+   return FH_BAD_VALUE; /* Card is not T or F? */
+}
+
+fh_result
+fh_get_int(HeaderUnit hu, const char* name, int* value)
+{
+   HeaderUnitStruct* list = FH_HU(hu);
+   char* endptr;
+   const char* p;
+
+   if (!list || !name || !value) return FH_INVALID; /* Bad arguments */
+   if (!(p = get_value(list, name))) return FH_NOT_FOUND;
+   *value = (int)strtol(p, &endptr, 0);
+   if (endptr && *endptr==' ') return FH_SUCCESS;
+   return FH_BAD_VALUE; /* Card is not an integer? */
+}
+
+fh_result
+fh_get_flt(HeaderUnit hu, const char* name, double* value)
+{
+   HeaderUnitStruct* list = FH_HU(hu);
+   char* endptr;
+   const char* p;
+
+   if (!list || !name || !value) return FH_INVALID; /* Bad arguments */
+   if (!(p = get_value(list, name))) return FH_NOT_FOUND;
+   *value = strtod(p, &endptr);
+   if (endptr && *endptr==' ') return FH_SUCCESS;
+   return FH_BAD_VALUE; /* Card is not an valid double? */
+}
+
+fh_result
+fh_get_str(HeaderUnit hu, const char* name, char* value, int maxlen)
+{
+   HeaderUnitStruct* list = FH_HU(hu);
+   fh_result rtn = FH_SUCCESS;
+   const char* p;
+   char* valp = value;
+   int len = 0;
+   int free_format = 0;
+
+   if (!list || !name || !value || !maxlen) return FH_INVALID;
+   if (!(p = get_value(list, name))) return FH_NOT_FOUND;
+   if (*p++!='\'') return FH_BAD_VALUE; /* Not a string */
+   while (1)
+   {
+      if (!*p) { rtn=FH_BAD_VALUE; break; } /* Unterminated string */
+      if (*p == '\'')
+      {
+	 if (p[1] == '\'') p++;	/* Literal single quote character in string? */
+	 else break;		/* Or actual end of string? */
+      }
+      if (++len > maxlen) { rtn=FH_BAD_VALUE; break; } /* value buf too small */
+      *valp++ = *p++;
+   }
+   /*
+    * Make sure there are no other characters (other than space) between
+    * the end of string and the start of the comment field.
+    */
+   if (*p == '\'')
+   {
+      /*
+       * Check for strings which are not in "fixed" format.
+       */
+      if (len < 8) free_format = 1;
+      while (*(++p) == ' ');
+      if (*p != '\0' && *p != '/') rtn=FH_BAD_VALUE;
+   }
+   while (len && *(valp - 1)==' ')
+   { valp--; len--; } /* Trim white space off end, but not beginning */
+   *valp++ = '\0';
+   if (free_format && rtn == FH_SUCCESS)
+   {
+      static int warned = 0;
+      int tmp;
+      
+      if (!warned)
+      {
+	 log_warning("converting strings to \"fixed\" format");
+	 warned = 1;
+      }
+      tmp = list->set_all_units; /* === Hack to prevent EHU's from being affected. */
+      list->set_all_units = 0;
+      fh_set_str(hu, 0, name, value, 0);
+      list->set_all_units = tmp;
+   }
+   return rtn;
+}
+
+fh_result
+fh_search(HeaderUnit hu, const char* name, double* idx)
+{
+   HeaderUnitStruct* list = FH_HU(hu);
+   FitsCardPtr hdr;
+
+   if (!list || !name) return FH_INVALID;
+   hdr = get_hdr(list, name, 0);
+   if (!hdr) return FH_NOT_FOUND;
+   if (idx) *idx = hdr->idx;
+   return FH_SUCCESS;
+}
+
+double
+fh_idx_after(HeaderUnit hu, const char* name)
+{
+   double idx;
+
+   if (fh_search(hu, name, &idx) == FH_SUCCESS)
+      return idx + 0.00001;
+   else
+      return 0.0;
+}
+
+double
+fh_idx_before(HeaderUnit hu, const char* name)
+{
+   double idx;
+
+   if (fh_search(hu, name, &idx) == FH_SUCCESS)
+      return idx - 0.00001;
+   else
+      return 0.0;
+}
+
+fh_result
+fh_remove(HeaderUnit hu, const char* name)
+{
+   HeaderUnitStruct* list = FH_HU(hu);
+   double idx = 0.; /* For future use? */
+   int i;
+
+   if (!list || !name) return FH_INVALID;
+
+   for (i = 0; i < list->len; i++)
+   {
+      if (fh_cmp(list->hdr[i]->card, name)>=MATCH_KEYWORD &&
+	  (idx == 0 || list->hdr[i]->idx == idx))
+      {
+	 free(list->hdr[i]);
+	 list->hdr[i] = 0;
+	 /*
+	  * Shift the rest of the list (this only has to copy pointers)
+	  */
+	 for (i++ ; i < list->len; i++)
+	    list->hdr[i-1] = list->hdr[i];
+	 list->len--;
+	 return FH_SUCCESS;
+      }
+   }
+   return FH_NOT_FOUND;
+}
+
+int
+fh_extensions(HeaderUnit hu)
+{
+   HeaderUnitStruct* list = FH_HU(hu);
+   fh_bool extend;
+   int nextend;
+
+   if (!list)
+   {
+      log_error("invalid argument to fh_extensions()");
+      return 0;
+   }
+   if (list->extensions != -1)
+      return list->extensions;
+   if (fh_get_bool(list, "EXTEND", &extend)!=FH_SUCCESS ||
+       fh_get_int(list, "NEXTEND", &nextend)!=FH_SUCCESS)
+      nextend = 0;
+
+   list->extensions = nextend;
+   return nextend;
+}
+
+int
+fh_header_blocks(HeaderUnit hu)
+{
+   HeaderUnitStruct* list = FH_HU(hu);
+   int rtn;
+
+   if (!list)
+   {
+      log_error("invalid argument to fh_header_blocks()");
+      return 0;
+   }
+
+   /*
+    * Calculate the minimum number of header blocks required to store
+    * all of the keywords plus an END line.
+    */
+   rtn = list->len + 1; /* +1 is for the END line */
+   if (list->reserve)
+   {
+      /*
+       * If reserve keywords are requested, the header is written with
+       * special COMMENT lines and the way END is written is also changed:
+       * the END line is never placed in the very last slot.  Instead, it
+       * always goes in the second-to-last slot (to avoid triggering bugs
+       * in other FITS reading software that may have a problem with this
+       * boundary condition.)
+       */
+      rtn +=
+	 list->reserve + 1; /* +1 is for the extra blank line at the end */
+   }
+   rtn = (rtn * FH_CARD_SIZE + FH_BLOCK_SIZE - 1) / FH_BLOCK_SIZE;
+
+   /*
+    * Bump size up to previous size.  It is possible to remove enough
+    * keywords that the header shrinks by a block, but we never actually
+    * write back a smaller header (for one thing, fh_rewrite() wouldn't
+    * work at all if we did.)
+    */
+   if (rtn < list->fd_header_blocks)
+      rtn = list->fd_header_blocks;
+
+   return rtn;
+}
+
+static int
+fh_xtension(HeaderUnit hu)
+{
+   HeaderUnitStruct* list = FH_HU(hu);
+   if (list->xtension == -1)
+   {
+      char tmp[80];
+      if (fh_get_str(list, "XTENSION", tmp, sizeof(tmp)) != FH_SUCCESS)
+	 list->xtension = FH_XTENSION_UNKNOWN;
+      else if (!strcmp(tmp, "IMAGE"))
+	 list->xtension = FH_XTENSION_IMAGE;
+      else if (!strcmp(tmp, "TABLE"))
+	 list->xtension = FH_XTENSION_TABLE;
+      else
+	 list->xtension = FH_XTENSION_UNKNOWN;
+   }
+   return list->xtension;
+}
+
+static int
+fh_bitpix(HeaderUnit hu)
+{
+   HeaderUnitStruct* list = FH_HU(hu);
+
+   if (list->bitpix == -1)
+      if (fh_get_int(list, "BITPIX", &list->bitpix)!=FH_SUCCESS)
+	 list->bitpix = -1;
+
+   return list->bitpix;
+}
+
+static int
+fh_bytepix(HeaderUnit hu)
+{
+   switch (fh_bitpix(hu))
+   {
+      case 8:
+      case -8: return 1; break;
+      case 16:
+      case -16: return 2; break;
+      case 32:
+      case -32: return 4; break;
+      case 64:
+      case -64: return 8; break;
+      default:
+      {
+	 log_error("invalid or missing BITPIX card");
+	 return 0;
+      }
+   }
+}
+
+int
+fh_image_bytes(HeaderUnit hu)
+{
+   HeaderUnitStruct* list = FH_HU(hu);
+   int naxis = 0;
+   int n_bytes = 1;
+   int tmp;
+
+   if (!list)
+   {
+      log_error("invalid argument to fh_image_bytes()");
+      return 0;
+   }
+   if (list->image_bytes != -1)
+      return list->image_bytes;
+
+   if (fh_get_int(list, "NAXIS", &naxis)!=FH_SUCCESS || naxis<1 || naxis>999)
+      return 0;
+
+   while (naxis)
+   {
+      char naxis_hdr[9];
+
+      sprintf(naxis_hdr, "NAXIS%d", naxis);
+      if (fh_get_int(list, naxis_hdr, &tmp)!=FH_SUCCESS || tmp < 1)
+      {
+	 sprintf(err_buf, "missing %.900s card", naxis_hdr);
+	 log_error(err_buf);
+	 return 0;
+      }
+      n_bytes *= tmp;
+      naxis--;
+   }
+   n_bytes *= fh_bytepix(hu);
+   /*
+    * The following allows BINTABLE extensions to be copied correctly,
+    * even though libfh does not provide support for interpreting them.
+    */
+   if (fh_get_int(list, "PCOUNT", &tmp) == FH_SUCCESS)
+      n_bytes += tmp;
+   list->image_bytes = n_bytes;
+   list->image_bytes_left = n_bytes;
+   return n_bytes;
+}
+
+int
+fh_image_blocks(HeaderUnit hu)
+{
+   return ((fh_image_bytes(hu) + FH_BLOCK_SIZE - 1) / FH_BLOCK_SIZE);
+}
+
+fh_result
+fh_merge(HeaderUnit hu, const HeaderUnit source_)
+{
+   HeaderUnitStruct* list = FH_HU(hu);
+   const HeaderUnitStruct* source = FH_HU(source_);
+   int i = 0;
+   char* s;
+
+   if (!list || !source)
+   {
+      log_error("invalid argument passed to fh_merge()");
+      return FH_INVALID;
+   }
+
+   while (i < source->len)
+   {
+      double idxmatch;
+      char name[9] = "        ";
+
+      /*
+       * No `=' in the FITS card?  In that case, create a new entry
+       * unless the idx values match exactly.  Otherwise, leave idxmatch
+       * set to 0, which tells get_card() to re-use an old card if
+       * it had the same name.
+       */
+      if (source->hdr[i]->card[8] != '=' ||
+	  !memcmp(source->hdr[i]->card, "COMMENT ", 8) ||
+	  !memcmp(source->hdr[i]->card, "HISTORY ", 8) ||
+	  !memcmp(source->hdr[i]->card, "        ", 8))
+	 idxmatch = source->hdr[i]->idx;
+      else
+	 idxmatch = 0.;
+      memcpy(name, source->hdr[i]->card, 8);
+      s = get_card(list, source->hdr[i]->idx, name, idxmatch);
+      if (!s) return FH_NO_MEMORY;
+      memcpy(s, source->hdr[i]->card, FH_CARD_SIZE);
+      i++;
+   }
+   return FH_SUCCESS;
+}
+
+fh_result
+fh_read_keyword_buffer(HeaderUnit hu, const char* buffer, double idx, int fast)
+{
+   HeaderUnitStruct* list = FH_HU(hu);
+   char* s;
+   int cards = FH_BLOCK_SIZE/FH_CARD_SIZE;
+
+   if (!list || !buffer)
+   {
+      log_error("invalid argument passed to fh_read_keyword_buffer()");
+      return FH_INVALID;
+   }
+
+   while (cards)
+   {
+      s = get_card(list, idx, 0, 0); /* 0, 0 means no check for existing */
+      if (!s) return FH_NO_MEMORY;
+      idx = 0.0; /* Switch to auto-increment mode. */
+
+      do
+      {
+	 memcpy(s, buffer, FH_CARD_SIZE);
+	 buffer += FH_CARD_SIZE;
+	 cards --;
+	 if (!fast && !memcmp(s, FH_RESERVE, FH_RESERVE_LEN))
+	 {
+	    list->reserve_found++;
+	    continue; /* Skip FH_RESERVE lines */
+	 }
+	 break;
+      } while (1);
+
+      if (!fast && fix_characters(s, /*repair=*/0) != FH_SUCCESS)
+      {
+	 sprintf(err_buf, "FITS format error in card [%.80s]", s);
+	 log_error(err_buf);
+	 return FH_BAD_VALUE;
+      }
+      if (!memcmp(s, "END     ", 8))
+      {
+	 double idx_auto;
+	 int i;
+
+	 free(list->hdr[--list->len]); /* Remove the END */
+	 while (!fast && list->len &&
+		strspn(list->hdr[list->len-1]->card, " ")==FH_CARD_SIZE)
+	    free(list->hdr[--list->len]); /* Remove trailing blank lines
+					   * and FH_RESERVE lines
+					   */
+
+	 /*
+	  * fh_read() is the only case where get_card() is called without
+	  * knowing what the keyword name is.  In order to enforce proper
+	  * `idx' numbers for the reserved keywords, one pass through all
+	  * the entries must be made here to correct the values.
+	  */
+	 if (!fast)
+	 {
+	    for (i = 0; i < list->len; i++)
+	    {
+	       idx_auto = auto_idx(list->hdr[i]->card);
+	       if (idx_auto < 10.0) list->hdr[i]->idx = idx_auto;
+	    }
+	    /* === Also do padding check if !fast? */
+	 }
+	 return FH_END_OF_FILE; /* This block included END. */
+      }
+   }
+   return FH_SUCCESS; /* More to read... */
+}
+
+fh_result
+fh_read(HeaderUnit hu, int fd, double idx)
+{
+   HeaderUnitStruct* list = FH_HU(hu);
+   int bytes_read = 0;
+   char* s;
+   fh_result rtn = FH_SUCCESS;
+
+   if (!list)
+   {
+      log_error("invalid argument passed to fh_read()");
+      return FH_INVALID;
+   }
+
+   list->fd = fd;
+   list->off_hdrs = lseek(fd, 0, SEEK_CUR);
+   list->off_data = (off_t)-1; /* Gets set before returning FH_SUCCESS */
+   
+   while (1)
+   {
+      s = get_card(list, idx, 0, 0); /* 0, 0 means no check for existing */
+      if (!s) return FH_NO_MEMORY;
+      idx = 0.0; /* Switch to auto-increment mode. */
+
+      /* Get the next (non-reserve-space) FITS card */
+      do
+      {
+	 switch (read_file(fd, s, FH_CARD_SIZE))
+	 {
+	    case -1:
+	    {
+	       log_perror("failed to read FITS card");
+	       return FH_IN_ERRNO;
+	       break;
+	    }
+	    case 0:
+	    {
+	       /*
+		* For use in programs like fitspipe, it can be
+		* expected to have end of file occur, but not
+		* if some of the header was already found (i.e.,
+		* an error message is only printed if the EOF
+		* occurs in the middle of a header read, not at
+		* the beginning.)  Either way, FH_END_OF_FILE is
+		* returned so the caller can do with that as
+		* they wish.
+		*/
+	       if (bytes_read != 0)
+		  log_error("unexpected end of file");
+	       return FH_END_OF_FILE;
+	       break;
+	    }
+	    case FH_CARD_SIZE: break; /* success */
+	    default:
+	    {
+	       log_error("short read on FITS card");
+	       return FH_BAD_VALUE;
+	    }
+	 }
+	 bytes_read += FH_CARD_SIZE;
+	 if (!memcmp(s, FH_RESERVE, FH_RESERVE_LEN))
+	 {
+	    list->reserve_found++;
+	    continue; /* Skip FH_RESERVE lines */
+	 }
+	 break;
+      } while (1);
+
+      if (fix_characters(s, /*repair=*/0) != FH_SUCCESS)
+      {
+	 sprintf(err_buf, "FITS format error in card [%.80s]", s);
+	 log_error(err_buf);
+	 rtn = FH_BAD_VALUE;
+      }
+      if (!memcmp(s, "END     ", 8))
+      {
+	 int first_padding = 1;
+	 double idx_auto;
+	 int i;
+
+	 free(list->hdr[--list->len]); /* Remove the END */
+	 while (list->len &&
+		strspn(list->hdr[list->len-1]->card, " ")==FH_CARD_SIZE)
+	    free(list->hdr[--list->len]); /* Remove trailing blank lines
+					   * and FH_RESERVE lines
+					   */
+
+	 /*
+	  * fh_read() is the only case where get_card() is called without
+	  * knowing what the keyword name is.  In order to enforce proper
+	  * `idx' numbers for the reserved keywords, one pass through all
+	  * the entries must be made here to correct the values.
+	  */
+	 for (i = 0; i < list->len; i++)
+	 {
+	    idx_auto = auto_idx(list->hdr[i]->card);
+	    if (idx_auto < 10.0) list->hdr[i]->idx = idx_auto;
+	 }
+
+	 /*
+	  * Verify the padding.
+	  */
+	 while ((bytes_read % FH_BLOCK_SIZE)!=0)
+	 {
+	    char tmp[FH_CARD_SIZE+1];
+	    int rd;
+	    
+	    rd = read_file(fd, tmp, FH_CARD_SIZE);
+	    if (rd == -1)
+	    {
+	       log_perror("FITS cards not properly padded");
+	       return FH_IN_ERRNO;
+	    }
+	    tmp[FH_CARD_SIZE] = '\0';
+	    if (rd == 0 && first_padding)
+	    {
+	       /*
+		* If there is no padding at all, this is OK (might be useful
+		* for generating templates which this library needs to read.)
+		* However, don't set fd_header_blocks... just return here.
+		* That way, the library will refuse to *update* one of these
+		* truncated header files, but can read in the headers without
+		* complaints.
+		*/
+	       return rtn;
+	    }
+	    if (rd != FH_CARD_SIZE)
+	    {
+	       log_error("FITS cards not properly padded");
+	       return FH_BAD_PADDING;
+	    }
+	    bytes_read += FH_CARD_SIZE;
+	    first_padding = 0;
+
+	    if (strspn(tmp, " ") != FH_CARD_SIZE)
+	    {
+	       log_warning("header padding contains characters other than ' '");
+	       return FH_BAD_PADDING;
+	    }
+	 }
+	 list->off_data = lseek(fd, 0, SEEK_CUR);
+	 list->fd_header_blocks = bytes_read / FH_BLOCK_SIZE;
+	 return rtn;
+      }
+   }
+   return FH_INVALID; /* Not reached */
+}
+
+
+fh_result
+fh_reserve(HeaderUnit hu, int n)
+{
+   HeaderUnitStruct* list = FH_HU(hu);
+
+   if (!list)
+   {
+      log_error("invalid argument to fh_reserve()");
+      return FH_INVALID;
+   }
+
+   list->reserve = n;
+   return FH_SUCCESS;
+}
+
+fh_result
+fh_rewrite(HeaderUnit hu)
+{
+   HeaderUnitStruct* list = FH_HU(hu);
+
+   fh_result rtn;
+
+   if (!list)
+   {
+      log_error("invalid argument to fh_rewrite()");
+      return FH_INVALID;
+   }
+
+   if (list->counting_cards)
+   {
+      show_card_count(hu);
+      return FH_SUCCESS;
+   }
+
+   if (!(list->fd_header_blocks) ||
+       (list->fd == -1))
+   {
+      log_error("fh_rewrite only valid on headers created from fh_read()");
+      return FH_INVALID;
+   }
+
+   /*
+    * First rewrite all extensions too, if applicable.
+    */
+   if (list->set_all_units)
+   {
+      fh_result tmp;
+      int ext;
+
+      for (ext = 1; ext <= fh_extensions(list); ext++)
+         if ((tmp = fh_rewrite(fh_ehu(hu, ext))) != FH_SUCCESS)
+	    return tmp;
+   }
+
+   /*
+    * If the number of blocks that will be written isn't still
+    * equal to the number of blocks found in the original file,
+    * it won't fit.  Return FH_NO_SPACE error.
+    */
+   if (fh_header_blocks(list) != list->fd_header_blocks)
+   {
+      sprintf(err_buf,
+	      "need %d blocks for new header, have %d; "
+	      "increase upstream fh_reserve()",
+	      fh_header_blocks(list), list->fd_header_blocks);
+      log_error(err_buf);
+      return FH_NO_SPACE;
+   }
+
+   /*
+    * Seek to the start of the old headers.
+    */
+   if (seek_file(list->fd, list->off_hdrs) != 0)
+      return FH_IN_ERRNO; /* seek_file() prints its own error message */
+   /* === IN_ERRNO is not always correct here ! */
+
+   /*
+    * Write the new header block.
+    */
+   rtn = fh_write(list, list->fd); /* fh_write prints its own error message */
+
+   /*
+    * Just a quick sanity check... make sure that the file is now back at the start
+    * of the data.  This should always be the case, unless there is a bug in this library.
+    */
+   if (rtn == FH_SUCCESS)
+   {
+      if (lseek(list->fd, 0, SEEK_CUR) != list->off_data)
+      {
+	 log_error("not at start of data after updating headers");
+	 return FH_INTERNAL_ERROR;
+      }
+   }
+
+   if (fh_unlock_file(hu) == -1)
+      log_perror("failed to unlock file");
+
+   return rtn;
+}
+
+static fh_result
+check_latent_errors(HeaderUnitStruct* list)
+{
+   /*
+    * The fh_set() functions do not return an errors.  However,
+    * they do set a flag for the rare case that memory allocation
+    * should fail, or if they were called with invalid arguments
+    * (NULL pointers, etc.)  fh_write*() flushes out these errors.
+    */
+   if (list->err_memory)
+   {
+      list->err_memory = list->err_invalid = 0;
+      return FH_NO_MEMORY;
+   }
+   if (list->err_invalid)
+   {
+      list->err_memory = list->err_invalid = 0;
+      return FH_INVALID;
+   }
+   return FH_SUCCESS;
+}
+
+fh_result
+fh_write_keyword_buffer(HeaderUnit hu, char* buffer, int* fits_blocks_inout)
+{
+   HeaderUnitStruct* list = FH_HU(hu);
+   char* writebuf = buffer;
+   int i = 0, writelen;
+   int blocks_left;
+   fh_result result;
+
+   /*
+    * Check for valid arguments.
+    */
+   if (!list || !buffer ||
+       !fits_blocks_inout || *fits_blocks_inout < 1)
+   {
+      log_error("invalid argument to fh_write_keyword_buffer()");
+      return FH_INVALID;
+   }
+
+   result = check_latent_errors(list);
+   if (result != FH_SUCCESS) return result;
+
+   fh_sort(list);
+   blocks_left = fh_header_blocks(list);
+
+   /*
+    * Truncate the header if it will not fit.
+    */
+   if (blocks_left > *fits_blocks_inout)
+   {
+      blocks_left = *fits_blocks_inout;
+      result = FH_NO_SPACE; /* At the end, return this error/warning */
+   }
+   else
+   {
+      *fits_blocks_inout = blocks_left;
+   }
+
+   while (blocks_left--)
+   {
+      /*
+       * Initialize the header block with spaces (' ' character.)
+       */
+      memset(writebuf, ' ', FH_BLOCK_SIZE);
+
+      /*
+       * If there are headers remaining in the list (i < list->len)
+       * then copy them in until the entire block is filled.  Obviously
+       * FH_BLOCK_SIZE better be divisible by FH_CARD_SIZE (it is.)
+       */
+      for (writelen = 0;
+	   writelen < FH_BLOCK_SIZE;
+	   writelen += FH_CARD_SIZE)
+      {
+	 if (i < list->len)
+	    memcpy(writebuf + writelen, list->hdr[i++]->card, FH_CARD_SIZE);
+      }
+      /*
+       * Last block? Add END plus a blank line in the last two slots.
+       * This relies on the calculation of fh_header_blocks() being
+       * correct, and having left at least two empty slots at the
+       * end of the final blocks.
+       */
+      if (!blocks_left)
+      {
+	 memset(writebuf + FH_BLOCK_SIZE - 2 * FH_CARD_SIZE, ' ', 2 * FH_CARD_SIZE);
+	 memcpy(writebuf + FH_BLOCK_SIZE - 2 * FH_CARD_SIZE, "END", 3);
+      }
+      writebuf += FH_BLOCK_SIZE;
+   }
+   return result;
+}
+
+fh_result
+fh_write(HeaderUnit hu, int fd)
+{
+   HeaderUnitStruct* list = FH_HU(hu);
+   char writebuf[FH_BLOCK_SIZE];
+   int i = 0, writelen;
+   int blocks_left;
+   fh_result result;
+
+   if (!list)
+   {
+      log_error("invalid argument to fh_write()");
+      return FH_INVALID;
+   }
+
+   if (list->counting_cards)
+   {
+      show_card_count(hu);
+      return FH_SUCCESS;
+   }
+
+   result = check_latent_errors(list);
+   if (result != FH_SUCCESS) return result;
+
+   list->off_hdrs = lseek(fd, 0, SEEK_CUR);
+
+   fh_sort(list);
+
+   blocks_left = fh_header_blocks(list);
+
+   while (blocks_left--)
+   {
+      /*
+       * Initialize the header block with spaces (' ' character.)
+       */
+      memset(writebuf, ' ', sizeof(writebuf));
+
+      /*
+       * If there are headers remaining in the list (i < list->len)
+       * then copy them in until the entire block is filled.  Obviously
+       * FH_BLOCK_SIZE better be divisible by FH_CARD_SIZE (it is.)
+       */
+      for (writelen = 0;
+	   writelen < sizeof(writebuf);
+	   writelen += FH_CARD_SIZE)
+      {
+	 if (i < list->len)
+	    memcpy(writebuf + writelen, list->hdr[i++]->card, FH_CARD_SIZE);
+	 /*
+	  * Reserve COMMENT lines get inserted in three cases:
+	  * 1) They were requested with fh_reserve().
+	  * 2) They were found in the existing FITS header.
+	  * 3) A header without any is being re-written with LESS blocks
+	  *    such that the END would land in the wrong block.  (Which
+	  *    only happens if the program deleted a bunch of keywords.)
+	  */	  
+	 else if (list->reserve || list->reserve_found ||
+		  ((i == list->len) && (blocks_left > 0)))
+	    memcpy(writebuf + writelen, FH_RESERVE, FH_RESERVE_LEN);
+	 else if (i == list->len)
+	 {
+	    memcpy(writebuf + writelen, "END", 3);
+	    i++;
+	 }
+      }
+      /*
+       * Last block? Add END plus a blank line in the last two slots.
+       * This relies on the calculation of fh_header_blocks() being
+       * correct, and having left at least two empty slots at the
+       * end of the final blocks.
+       *
+       * This only happens for the case with the special "reserve" comments.
+       * If list->reserve == 0, then the END was already included, immediately
+       * after the last keyword.
+       */
+      if (!blocks_left && (list->reserve || list->reserve_found))
+      {
+	 memset(writebuf + sizeof(writebuf) - 2 * FH_CARD_SIZE, ' ', 2 * FH_CARD_SIZE);
+	 memcpy(writebuf + sizeof(writebuf) - 2 * FH_CARD_SIZE, "END", 3);
+      }
+      if (write_file(fd, writebuf, sizeof(writebuf)) != sizeof(writebuf))
+      {
+	 log_perror("write failed");
+	 return FH_IN_ERRNO;
+      }
+   }
+   /*
+    * Remember the data offset.
+    */
+   list->off_data = lseek(fd, 0, SEEK_CUR);
+
+   /*
+    * Remember the number of blocks written for potential fh_rewrite() later.
+    */
+   list->fd_header_blocks = fh_header_blocks(list);
+
+   /*
+    * Remember the file descriptor for potential mmap later.
+    */
+   if (list->fd == -1)
+      list->fd = fd;
+   return FH_SUCCESS;
+}
+
+
+fh_result
+fh_read_padding(HeaderUnit hu, int fd)
+{
+   HeaderUnitStruct* list = FH_HU(hu);
+   int padding_bytes = fh_image_blocks(hu) * FH_BLOCK_SIZE - fh_image_bytes(hu);
+   int bytes_read;
+   char buf[FH_BLOCK_SIZE];
+   char* p = buf;
+   char padding = '\0';
+
+   if (!list)
+   {
+      log_error("invalid HeaderUnit passed to fh_read_padding");
+      return FH_INVALID;
+   }
+
+   if (list->image_bytes_left)
+   {
+      log_error("fh_read_padding attempted with pixels left to read");
+      return FH_BAD_SIZE;
+   }
+
+   if (padding_bytes)
+   {
+      bytes_read = read_file(fd, buf, padding_bytes);
+      if (bytes_read < 0)
+      {
+	 log_perror("failed to read padding");
+	 return FH_IN_ERRNO;
+      }
+      if (bytes_read != padding_bytes)
+      {
+	 log_error("not enough padding");
+	 return FH_END_OF_FILE;
+      }
+   }
+   if (fh_xtension(hu) == FH_XTENSION_TABLE) padding = ' ';
+   while (padding_bytes--)
+      if (*p++ != padding)
+      {
+	 if (fh_xtension(hu) == FH_XTENSION_TABLE)
+	    log_error("padding contains characters other than ' '");
+	 else
+	    log_error("padding contains characters other than \\0");
+	 return FH_BAD_PADDING;
+      }
+
+   return FH_SUCCESS;
+}
+
+/* Simple run-time test for the host byte-order, and swapping routines. */
+static int _endian_test = 1;
+#define is_little_endian() (*(char*)&_endian_test)
+static void fh_chsign(unsigned short* buffer, int elements)
+{
+#if 0
+   if (is_little_endian()) /* %%% Or is it always 0x0080 ? */
+      while (elements--)
+	 *buffer++ ^= 0x8000;
+   else
+#else
+   while (elements--)
+      *buffer++ ^= 0x0080;
+#endif
+}
+static void fh_bswap2(unsigned short* buffer, int elements)
+{
+   register unsigned short temp;
+
+   while (elements--)
+   {
+      temp = *buffer;
+      *buffer    = (temp & 0xff00) >> 8;
+      *buffer++ |= (temp & 0x00ff) << 8;
+   }
+}
+static void fh_bswap2chsign(unsigned short* buffer, int elements)
+{
+   register unsigned short temp;
+
+   while (elements--)
+   {
+      temp = *buffer;
+      *buffer    = ((temp^0x8000) & 0xff00) >> 8;
+      *buffer++ |= (temp & 0x00ff) << 8;
+   }
+}
+static void fh_bswap4(unsigned long* buffer, int elements)
+{
+   register unsigned long temp;
+
+   while (elements--)
+   {
+      temp = *buffer;
+      *buffer    =  (temp & 0xff000000) >> 24;
+      *buffer   |= (temp & 0x00ff0000) >> 8;
+      *buffer   |= (temp & 0x0000ff00) << 8;
+      *buffer++ |= (temp & 0x000000ff) << 24;
+   }
+}
+static void fh_bswap8(unsigned long* buffer, int elements)
+{
+   register unsigned temp1, temp2;
+
+   while (elements--)
+   {
+      temp1 = *buffer;
+      temp2 = *(buffer + 1);
+      *buffer    =  (temp2 & 0xff000000) >> 24;
+      *buffer   |= (temp2 & 0x00ff0000) >> 8;
+      *buffer   |= (temp2 & 0x0000ff00) << 8;
+      *buffer++ |= (temp2 & 0x000000ff) << 24;
+      *buffer    =  (temp1 & 0xff000000) >> 24;
+      *buffer   |= (temp1 & 0x00ff0000) >> 8;
+      *buffer   |= (temp1 & 0x0000ff00) << 8;
+      *buffer++ |= (temp1 & 0x000000ff) << 24;
+   }
+}
+
+fh_result
+fh_munmap_raw_image(HeaderUnit hu)
+{
+   HeaderUnitStruct* list = FH_HU(hu);
+   HeaderUnitStruct* primary = FH_HU(list->phu);
+
+   if (!primary) primary = list;
+   if (!list)
+   {
+      log_error("invalid argument to fh_munmap_raw_image()");
+      return FH_INVALID;
+   }
+
+   if (list->mmap_count == 0 || primary->mmap_count == 0)
+      return FH_SUCCESS; /* Don't complain if nothing to munmap? */
+
+   list->mmap_count--;
+   primary->mmap_count--;
+
+   if (primary->mmap_count == 0)
+   {
+      if (munmap(primary->mmap_addr, primary->file_size) != 0)
+      {
+	 log_error("munmap failed");
+	 return FH_IN_ERRNO;
+      }
+   }
+   return FH_SUCCESS;
+}
+
+fh_result
+fh_map_raw_image(HeaderUnit hu, void** data, int size)
+{
+   HeaderUnitStruct* list = FH_HU(hu);
+   HeaderUnitStruct* primary = FH_HU(list->phu);
+
+   if (!primary) primary = list;
+   if (!list || !data || !size)
+   {
+      log_error("invalid argument to fh_map_raw_image()");
+      return FH_INVALID;
+   }
+
+   if (list->fd == -1)
+   {
+      log_error("fh_map_raw_image attempted before fh_read");
+      return FH_INVALID;
+   }
+
+   if (fh_image_bytes(hu) == 0)
+   {
+      log_error("image size must be set by NAXIS/BITPIX keywords before mmaping");
+      return FH_BAD_SIZE;
+   }
+
+   fh_munmap_raw_image(hu); /* Remove any previous mapping. */
+
+   /*
+    * Calculate file_size now, if this is a newly created file.
+    * Upon a read, file_size is determined by a stat() call.
+    * When writing a new file, it is determined with the seek,
+    * below:
+    *
+    * %%% It doesn't seem to work.  Why?
+    */
+   if (list->file_size == 0)
+   {
+      list->file_size = lseek(list->fd, 0, SEEK_END);
+      if (list->file_size == (off_t)-1)
+      {
+	 log_perror("seek to end of file failed");
+	 return -1;
+      }
+   }
+
+   if (size != list->image_bytes)
+   {
+      sprintf(err_buf,
+	      "fh_map_raw_image size (%d) does not match image_bytes (%d)",
+	      size, list->image_bytes);
+      log_error(err_buf);
+      return FH_BAD_SIZE;
+   }
+   if (primary->mmap_count == 0)
+   {
+      primary->mmap_addr = mmap(0, primary->file_size,
+				PROT_READ|PROT_WRITE, MAP_SHARED, /* %%% ### The PROT_WRITE is only temporary */
+				list->fd, 0);
+      if (primary->mmap_addr == (void*)-1 ||
+	  primary->mmap_addr == (void*)0)
+      {
+	 log_perror("error: mmap failed");
+	 return FH_IN_ERRNO;
+      }
+   }
+
+   list->mmap_count++;
+   primary->mmap_count++;
+
+   *data = (char*)primary->mmap_addr + list->off_data;
+   return FH_SUCCESS;
+}
+
+fh_result
+fh_read_image(HeaderUnit hu, int fd, void* data, int size, int typesize)
+{
+   HeaderUnitStruct* list = FH_HU(hu);
+   int need_swap = 0;
+   int bytes_to_read, bytes_left;
+
+   if (!list || !data || fd == -1)
+   {
+      log_error("invalid argument to fh_read_image()");
+      return FH_INVALID;
+   }
+
+   if (is_little_endian()) need_swap = 1;		/* Auto */
+   if (typesize < 0) { need_swap = 1; typesize *= -1; }	/* Forced on */
+   else if (typesize < 2) need_swap = 0;		/* Forced off or N/A */
+
+   if (typesize && fh_bitpix(hu) != -1 && typesize != fh_bytepix(hu))
+   {
+      log_error("fh_read_image typesize/BITPIX mismatch");
+      return FH_BAD_SIZE;
+   }
+
+   if (fh_image_bytes(hu) == 0) /* Calculate image size if needed. */
+   {
+      log_error("image size must be set by NAXIS/BITPIX keywords before reading");
+      return FH_BAD_SIZE;
+   }
+
+   if (size > list->image_bytes_left)
+   {
+      sprintf(err_buf,
+	      "fh_read_image size (%d) is more than image_bytes_left (%d)",
+	      size, list->image_bytes_left);
+      log_error(err_buf);
+      return FH_BAD_SIZE;
+   }
+   bytes_left = size;
+   list->image_bytes_left -= size;
+
+   while (bytes_left)
+   {
+      bytes_to_read = bytes_left;
+      /* Do smaller reads to pipeline byte-swapping if need_swap is true */
+      if (need_swap && bytes_to_read > 32768) bytes_to_read = 32768;
+      if (read_file(fd, data, bytes_to_read) != bytes_to_read)
+      {
+	 log_perror("failed to read image data");
+	 return FH_IN_ERRNO;
+      }
+      if (need_swap) switch (typesize)
+      {
+	 case 2: fh_bswap2(data, bytes_to_read / 2); break;
+	 case 4: fh_bswap4(data, bytes_to_read / 4); break;
+	 case 8: fh_bswap8(data, bytes_to_read / 8); break;
+	 default:
+	 log_error("byte-swapping only defined for 2,4,8-byte data types");
+	 return FH_INVALID;
+      }
+      bytes_left -= bytes_to_read;
+      data = (char*)data + bytes_to_read;
+   }
+   return FH_SUCCESS;
+}
+
+fh_result
+fh_read_padded_image(HeaderUnit hu, int fd, void* data, int size, int typesize)
+{
+   fh_result rtn;
+
+   if (size &&
+       (rtn = fh_read_image(hu, fd, data, size, typesize)) != FH_SUCCESS)
+      return rtn;
+
+   return fh_read_padding(hu, fd);
+}
+
+fh_result
+fh_write_padding(HeaderUnit hu, int fd)
+{
+   HeaderUnitStruct* list = FH_HU(hu);
+   HeaderUnitStruct* primary = list?(FH_HU(list->phu)):0;
+   int padding_bytes;
+
+   if (!list || fd == -1)
+   {
+      log_error("invalid argument to fh_write_padding()");
+      return FH_INVALID;
+   }
+
+   padding_bytes = fh_image_blocks(hu) * FH_BLOCK_SIZE - fh_image_bytes(hu);
+
+   /*
+    * The library will only write the padding for two cases:
+    *
+    * 1) The library itself has been used to write all the data,
+    *    all at once or in segments, so image_bytes_left is 0.
+    * 2) The library has written none of the data (image_bytes_left
+    *    is equal to image_bytes) in which case it is assumed
+    *    that the data was written to the file by the user.
+    *
+    * The main point of this check is to catch alignment problems
+    * where not enough data is written to the FITS file before
+    * the padding was added.
+    */
+   if (list->image_bytes_left != 0 &&
+       list->image_bytes_left != fh_image_bytes(hu))
+   {
+      sprintf(err_buf,
+	      "fh_write_padding attempted but %d pixels left to write",
+	      list->image_bytes_left);
+      log_error(err_buf);
+      return FH_BAD_SIZE;
+   }
+
+   if (padding_bytes)
+   {
+      if (write_file(fd, padding_block(fh_xtension(hu)), padding_bytes)
+	  != padding_bytes)
+      {
+	 log_perror("failed to write padding");
+	 return FH_IN_ERRNO;
+      }
+   }
+   /* %%% ... */
+   if (list->file_size < list->off_data + fh_image_bytes(hu) + padding_bytes)
+      list->file_size = list->off_data + fh_image_bytes(hu) + padding_bytes;
+   if (primary && primary->file_size < list->file_size)
+      primary->file_size = list->file_size; /* ... %%% */
+
+   return FH_SUCCESS;
+}
+
+fh_result
+fh_write_image(HeaderUnit hu, int fd, void* data, int size, int typesize)
+{
+   HeaderUnitStruct* list = FH_HU(hu);
+   int need_swap = 0;
+   int need_chsign = 0;
+   int bytes_to_write, bytes_left;
+
+   if (is_little_endian()) need_swap = 1;		/* Auto */
+   if (typesize < 0) { need_swap = 1; typesize *= -1; }	/* Forced on */
+   else if (typesize < 2 ||				/* Forced off or N/A */
+	    typesize == FH_TYPESIZE_16URAW) need_swap = 0;
+   if (typesize == FH_TYPESIZE_16U ||			/* 16-bit unsigned */
+       typesize == FH_TYPESIZE_16URAW) { typesize = 2; need_chsign = 1; }
+
+   if (!list || !data || fd == -1)
+   {
+      log_error("invalid argument to fh_write_image()");
+      return FH_INVALID;
+   }
+   if (typesize && fh_bitpix(hu) != -1 && typesize != fh_bytepix(hu))
+   {
+      log_error("fh_write_padded_image typesize/BITPIX mismatch");
+      return FH_BAD_SIZE;
+   }
+   if (fh_image_bytes(hu) == 0) /* Calculate image size if needed. */
+   {
+      log_error("image size must be set by NAXIS/BITPIX keywords before writing");
+      return FH_BAD_SIZE;
+   }
+   if (size > list->image_bytes_left)
+   {
+      sprintf(err_buf,
+	      "fh_write_image size (%d) is more than image_bytes_left (%d)",
+	      size, list->image_bytes_left);
+      log_error(err_buf);
+      return FH_BAD_SIZE;
+   }
+   bytes_left = size;
+   list->image_bytes_left -= size;
+   if (!need_swap && !need_chsign)
+   {
+      if (write_file(fd, data, bytes_left) != bytes_left)
+      {
+	 log_perror("failed to write image data");
+	 return FH_IN_ERRNO;
+      }
+   }
+   else /* byte-swapping case requires a temporary buffer */
+   {
+      unsigned char outbuf[32768];
+
+      while (bytes_left)
+      {
+	 bytes_to_write = bytes_left;
+	 if (bytes_to_write > sizeof(outbuf)) bytes_to_write = sizeof(outbuf);
+	 memcpy(outbuf, data, bytes_to_write);
+	 switch (typesize + need_chsign)
+	 {
+	    case 2: fh_bswap2((void*)outbuf, bytes_to_write / 2); break;
+	    case 3: /* Special case that also converts unsign->sign (16-bit) */
+	    if (need_swap)
+	       fh_bswap2chsign((void*)outbuf, bytes_to_write / 2);
+	    else
+	       fh_chsign((void*)outbuf, bytes_to_write / 2);
+	    break;
+	    case 4: fh_bswap4((void*)outbuf, bytes_to_write / 4); break;
+	    case 8: fh_bswap8((void*)outbuf, bytes_to_write / 8); break;
+	    default:
+	    log_error("byte-swapping only defined for 2,4,8-byte data types");
+	    return FH_INVALID;
+	 }
+	 if (write_file(fd, outbuf, bytes_to_write) != bytes_to_write)
+	 {
+	    log_perror("failed to write image data");
+	    return FH_IN_ERRNO;
+	 }
+	 bytes_left -= bytes_to_write;
+	 data = (char*)data + bytes_to_write;
+      }
+   }
+   return FH_SUCCESS;
+}
+
+fh_result
+fh_reserve_padded_image(HeaderUnit hu, int fd)
+{
+   HeaderUnitStruct* list = FH_HU(hu);
+
+   if (!list || fd == -1)
+   {
+      log_error("invalid argument to fh_reserve_padded_image()");
+      return FH_INVALID;
+   }
+   if (list->off_data == 0 || list->off_data == (off_t)-1)
+   {
+      log_error("no header for fh_reserve_padded_image()");
+      return FH_INVALID;
+   }
+   if (fh_image_bytes(hu) == 0) /* Calculate image size if needed. */
+   {
+      log_error("image size must be set by NAXIS/BITPIX keywords before reserving image area");
+      return FH_BAD_SIZE;
+   }
+   if (seek_file(fd, list->off_data + list->image_bytes_left - 1) != 0)
+      return FH_IN_ERRNO; /* seek_file() prints its own error message */
+   if (write_file(fd, "\0", 1) != 1)
+   {
+      log_perror("reserve image failed");
+      return FH_IN_ERRNO; /* seek_file() prints its own error message */
+   }
+   list->image_bytes_left = 0;
+   return fh_write_padding(hu, fd);
+}
+
+fh_result
+fh_write_padded_image(HeaderUnit hu, int fd, void* data, int size, int typesize)
+{
+   fh_result rtn;
+
+   if (size &&
+       (rtn = fh_write_image(hu, fd, data, size, typesize)) != FH_SUCCESS)
+      return rtn;
+   
+   return fh_write_padding(hu, fd);
+}
+
+static fh_result
+fh_copy_padded_image_internal(HeaderUnit hu, int fd_out, int fd_in, int verify)
+{
+   HeaderUnitStruct* list = FH_HU(hu);
+   static char* buf = 0;
+   int copy_size;
+   int image_bytes;
+   fh_result rtn0 = FH_SUCCESS, rtn1, rtn2;
+   int zero_bytes;
+   char* p;
+
+
+   if (!list || fd_out == -1 || fd_in == -1)
+   {
+      log_error("invalid argument to fh_copy_padded_image()");
+      return FH_INVALID;
+   }
+   if (!buf &&
+       !(buf = (char*)malloc(FH_BUFFER_SIZE)))
+   {
+      log_error("out of memory in fh_copy_padded_image()");
+      return FH_NO_MEMORY;
+   }
+
+   image_bytes = fh_image_bytes(hu);
+
+   while (image_bytes)
+   {
+      int bytes;
+
+      copy_size = image_bytes;
+      if (copy_size > FH_BUFFER_SIZE) copy_size = FH_BUFFER_SIZE;
+      bytes = read_file(fd_in, buf, copy_size);
+      if (bytes == 0)
+      {
+	 log_error("fh_copy_padded_image unexpected EOF");
+	 return FH_END_OF_FILE;
+      }
+      if (bytes == -1)
+      {
+	 log_perror("fh_copy_padded_image read failed");
+	 return FH_IN_ERRNO;
+      }
+      if (bytes != copy_size)
+      {
+	 log_error("fh_copy_padded_image short read");
+	 return FH_BAD_VALUE;
+      }
+      if (verify) for (p = buf, zero_bytes = 0; p < buf + copy_size; p++)
+      {
+	 if (*p != '\0')
+	 {
+	    zero_bytes = 0;
+	    p = (char*)((unsigned int)p|511); /* next disk sector */
+	 }
+	 else if (++zero_bytes == 512)
+	 {
+	    /* %%% log_error? */
+	    fprintf(stderr, "warning: fhtool: file contains zero-block(s)\n");
+	    rtn0 = FH_BAD_VALUE;
+	    break;
+	 }
+      }
+      bytes = write_file(fd_out, buf, copy_size);
+      if (bytes != copy_size)
+      {
+	 log_perror("fh_copy_padded_image write failed");
+	 return FH_IN_ERRNO;
+      }
+      list->image_bytes_left -= copy_size;
+      image_bytes -= copy_size;
+   }
+
+   rtn1 = fh_read_padding(hu, fd_in);
+   rtn2 = fh_write_padding(hu, fd_out);
+   if (rtn2 != FH_SUCCESS) return rtn2;
+   if (rtn0 != FH_SUCCESS) return rtn0;
+   return rtn1;
+}
+
+fh_result
+fh_copy_padded_image(HeaderUnit hu, int fd_out, int fd_in)
+{
+   return fh_copy_padded_image_internal(hu, fd_out, fd_in, /*verify=*/0);
+}
+
+fh_result
+fh_copy_and_verify_padded_image(HeaderUnit hu, int fd_out, int fd_in)
+{
+   return fh_copy_padded_image_internal(hu, fd_out, fd_in, /*verify=*/1);
+}
+
+void
+fh_link_ehu_to_phu(HeaderUnit ehu, HeaderUnit phu)
+{
+   HeaderUnitStruct* ehu_list = FH_HU(ehu);
+   HeaderUnitStruct* phu_list = FH_HU(phu);
+
+   ehu_list->next = phu_list->ehu;
+   phu_list->ehu = ehu_list;
+   ehu_list->phu = phu_list;
+}
+
+static HeaderUnit
+ehu_internal(HeaderUnitStruct* phu, const char* extname, int imageid)
+{
+   int i = 0, found = 0;
+   off_t next_off;
+   HeaderUnit* next_ehu = &(phu->ehu);
+
+   if (phu == 0)
+   {
+      log_error("invalid argument to ehu function");
+      return 0;
+   }
+
+   if (phu->fd == -1)
+   {
+      log_error("no file associated with PHU");
+      return 0;
+   }
+
+   next_off = phu->off_data;
+
+   if (fh_extensions(phu)==0)
+   {
+      log_error("no extensions found in file");
+      return 0;
+   }
+
+   /*
+    * Search all extensions for `extname' or `imageid'
+    */
+   while (i++ < fh_extensions(phu))
+   {
+      if (!*next_ehu)
+      {
+	 /*
+	  * If this is the first pass, read the EHU into the cache.
+	  */
+	 *next_ehu = fh_create();
+	 FH_HU(*next_ehu)->phu = phu; /* Link to phu for mmap re-use */
+	 if (seek_file(phu->fd, next_off) != 0) return 0;
+	 if (fh_read(*next_ehu, phu->fd, 0) != FH_SUCCESS)
+	 {
+	    log_error("problem with MEF structure");
+	    return 0; /* File error? */
+	 }
+      }
+      /*
+       * Now look for either a matching EXTNAME or IMAGEID card.
+       */
+      if (extname)
+      {
+	 char n[FH_MAX_STRLEN + 1];
+
+	 if (fh_get_str(*next_ehu, "EXTNAME", n, sizeof(n)) == FH_SUCCESS &&
+	     fh_cmp(n, extname)>=MATCH_FULL)
+	    found = 1;
+      }
+      else if (imageid >= 0)
+      {
+	 int id;
+
+	 if (fh_get_int(*next_ehu, "IMAGEID", &id) == FH_SUCCESS &&
+	     id == imageid)
+	    found = 1;
+      }
+      else
+      {
+	 if (i == -imageid)
+	    found = 1;
+      }
+
+      if (found)
+      {
+	 if (seek_file(phu->fd, FH_HU(*next_ehu)->off_data) != 0)
+	    return 0;
+	 return *next_ehu; /* Success! */
+      }
+
+      /*
+       * Set the offset to the beginning of the next extension (but don't
+       * actually seek there unless the EHU hasn't been cached yet.)
+       */
+      next_off = FH_HU(*next_ehu)->off_data +
+	 fh_image_blocks(*next_ehu) * FH_BLOCK_SIZE;
+      next_ehu = &(FH_HU(*next_ehu)->next);
+   }
+   return 0; /* Not found */
+}
+
+HeaderUnit
+fh_ehu(HeaderUnit phu, int number)
+{
+   HeaderUnitStruct* list = FH_HU(phu);
+   
+   if (number == 0 && list)
+   {
+      if (seek_file(list->fd, list->off_data) != 0)
+	 return 0;
+      list->image_bytes_left = fh_image_bytes(phu);
+      return (HeaderUnit)list;
+   }
+   return ehu_internal(list, 0, -number);
+}
+
+HeaderUnit
+fh_ehu_by_imageid(HeaderUnit phu, int imageid)
+{
+   return ehu_internal(FH_HU(phu), 0, imageid);
+}
+
+HeaderUnit
+fh_ehu_by_extname(HeaderUnit phu, const char* extname)
+{
+   return ehu_internal(FH_HU(phu), extname, 0);
+}
+
+#if 0
+/* %%% Decide whether to keep this or not.*/
+typedef fh_result (*fh_handler)(double idx, const char* card);
+
+fh_result
+fh_foreach(HeaderUnit hu, fh_handler handler)
+{
+   HeaderUnitStruct* list = FH_HU(hu);
+   fh_result rtn = FH_SUCCESS;
+   int i;
+
+   for (i = 0; i < list->len ; i++)
+      if (handler(list->hdr[i]->idx, handler) != FH_SUCCESS)
+
+
+   return rtn;
+}
+#endif
+
+fh_result
+fh_show(HeaderUnit hu)
+{
+   HeaderUnitStruct* list = FH_HU(hu);
+   int i;
+
+   for (i = 0; i < list->len ; i++)
+      printf("%.79s\n", list->hdr[i]->card);
+
+   return FH_SUCCESS;
+}
+
+/* -------------------------------------------------------------------------
+ *             Implementation of internal utility functions:
+ * -------------------------------------------------------------------------
+ */
+static const char*
+padding_block(int type)
+{
+   static int s_initialized = 0;
+   static char s[FH_BLOCK_SIZE];
+   static int z_initialized = 0;
+   static char z[FH_BLOCK_SIZE];
+
+   if (type == FH_XTENSION_TABLE) /* FITS ASCII TABLES are padded with ' ' */
+   {
+      if (!s_initialized)
+      {
+	 memset(s, ' ', sizeof(s));
+	 s_initialized = 1;
+      }
+      return s;
+   }
+
+   if (!z_initialized) /* FITS data and IMAGE extensions are padded with \0 */
+   {
+      memset(z, 0, sizeof(z));
+      z_initialized = 1;
+   }
+   return z;
+}
+
+static int
+write_file(int fd, const void* buf, int len)
+{
+   int rtn;
+   int count = 0;
+
+   while (len)
+   {
+      rtn = write(fd, (char*)buf, len);
+
+      switch (rtn)
+      {
+	 case -1: if (errno == EINTR || errno == EAGAIN)
+	    continue; /* retry */
+	    return -1; /* permanent failure */
+	 case 0: return count;
+	 default:
+	 {
+	    len -= rtn;
+	    count += rtn;
+	    buf = (char*)buf + rtn;
+	 }
+      }
+   }
+   return count;
+}
+
+static int
+read_file(int fd, const void* buf, int len)
+{
+   int rtn;
+   int count = 0;
+
+   while (len)
+   {
+      rtn = read(fd, (char*)buf, len);
+
+      switch (rtn)
+      {
+	 case -1: if (errno == EINTR || errno == EAGAIN)
+	    continue; /* retry */
+	    return -1; /* permanent failure */
+	 case 0: return count;
+	 default:
+	 {
+	    len -= rtn;
+	    count += rtn;
+	    buf = (char*)buf + rtn;
+	 }
+      }
+   }
+   return count;
+}
+
+static int
+seek_file(int fd, off_t off)
+{
+   if (off == (off_t)-1)
+   {
+      log_error("cannot seek in input");
+      return -1;
+   }
+
+   if (lseek(fd, off, SEEK_SET) == (off_t)-1)
+   {
+      log_perror("seek failed");
+      return -1;
+   }
+   return 0;
+}
+
+static int
+fh_unlock_file(HeaderUnit hu)
+{
+   HeaderUnitStruct* list = FH_HU(hu);
+   int rtn;
+
+   if (list->fl.l_type != F_UNLCK && list->fd_locked != -1)
+   {
+      list->fl.l_type = F_UNLCK;
+      do
+      {
+         rtn = fcntl(list->fd_locked, F_SETLKW, &list->fl);
+      } while (rtn == -1 && errno == EINTR);
+      return rtn;
+   }
+   return 0;
+}
+
+static int
+fh_lock_file(HeaderUnit hu, int fd, fh_mode mode)
+{
+   HeaderUnitStruct* list = FH_HU(hu);
+   int rtn;
+
+   fh_unlock_file(hu); /* Remove any old lock, if needed */
+   list->fd_locked = fd;
+   list->fl.l_type = (mode == FH_FILE_RDONLY)? F_RDLCK : F_WRLCK;
+   list->fl.l_whence = SEEK_SET;
+   list->fl.l_start = 0;
+   list->fl.l_len = 0;
+   do
+   {
+      rtn = fcntl(fd, F_SETLKW, &list->fl);
+   } while (rtn == -1 && errno == EINTR);
+   return rtn;
+}
+
+
+static fh_result
+fix_characters(char* s, int repair)
+{
+   int col;
+   int bad_value = 0;
+
+   for (col=0; col<FH_NAME_SIZE; col++)
+   {
+      if (s[col] == ' ')
+      {
+	 while (++col < FH_NAME_SIZE)
+	    if (s[col] != ' ') { if (repair) s[col] = ' '; bad_value++; }
+	 break;
+      }
+      if (s[col] != NAME_CHR(s[col]))
+      { if (repair) s[col] = NAME_CHR(s[col]); bad_value++; }
+   }
+   
+   for (col=FH_NAME_SIZE; col<FH_CARD_SIZE; col++)
+      if (s[col] < ' ' || s[col] >= 127) { if (repair) s[col] = '_'; bad_value++; }
+
+   if (bad_value) return FH_BAD_VALUE;
+   return FH_SUCCESS;
+}
+
+/*
+ * See if "name1" is the same as "name2".
+ *
+ * Only the first 8 characters are compared, or up to a '\0'.
+ * If one name contains a '\0', the other must contain only
+ * ' ' characters (or also a a '\0') for the match to be
+ * successful.
+ */
+static match_result
+fh_cmp(const char* name1, const char* name2)
+{
+   int i;
+
+   if (!name1 || !name2) return MATCH_FAILED;
+   for (i = 0; i < FH_CARD_SIZE; i++)
+   {
+      if (NAME_CHR(*name1) != NAME_CHR(*name2))
+      {
+         /* mismatch found */
+         if (i >= FH_NAME_SIZE) return MATCH_KEYWORD;
+         if (!*name2) return MATCH_SUBSTR;
+         return MATCH_FAILED;
+      }
+      if (*name1) name1++;
+      if (*name2) name2++;
+   }
+   return MATCH_FULL;
+}
+
+static int
+fh_compare(const void* a, const void* b)
+{
+   double diff = (*((FitsCard**)a))->idx - (*((FitsCard**)b))->idx;
+
+   if (diff < 0) return -1;
+   else if (diff > 0) return 1;
+   else return 0;
+}
+
+static void
+fh_sort(HeaderUnit hu)
+{
+   HeaderUnitStruct* list = FH_HU(hu);
+
+   if (list->len <= 0) return; /* No keywords to sort! */
+
+   qsort(list->hdr, list->len, sizeof(FitsCardPtr), fh_compare);
+}
+
+/*
+ * Passing a 0 for `idx' means match only by `name'
+ */
+static FitsCardPtr
+get_hdr(HeaderUnitStruct* list, const char* name, double idx)
+{
+   FitsCardPtr hdr;
+   int i;
+
+   for (i = 0; i < list->len; i++)
+   {
+      hdr = list->hdr[i];
+      if (fh_cmp(hdr->card, name) >= MATCH_KEYWORD &&
+	  (idx == 0 || hdr->idx == idx))
+	 return hdr;
+   }
+   return 0;
+}
+
+static const char*
+get_value(HeaderUnitStruct* list, const char* name)
+{
+   FitsCardPtr hdr = get_hdr(list, name, 0);
+   const char* p;
+   fh_bool inherit;
+
+   if (!hdr && name &&
+       strcmp(name, "EXTEND") &&
+       strcmp(name, "NEXTEND") &&
+       strcmp(name, "INHERIT"))
+   {
+      if (list->phu && name &&
+	  fh_get_bool(list, "INHERIT", &inherit)==FH_SUCCESS &&
+	  inherit==FH_TRUE)
+      {
+	 /* %%% Only do this if INHERIT=T */
+	 hdr = get_hdr(list->phu, name, 0);
+      }
+   }
+   if (!hdr) return 0; /* Not found. */
+
+   p = hdr->card + FH_NAME_SIZE;
+   if (*p++ != '=') return 0; /* Something wrong with FITS file? */
+   while (*p == ' ') p++;
+   return p;
+}
+
+static double
+auto_idx(const char* name)
+{
+   if (!name) return 10.0;
+   if (fh_cmp(name, "SIMPLE")  >=MATCH_KEYWORD)	return 0.0;
+   if (fh_cmp(name, "XTENSION")>=MATCH_KEYWORD)	return 0.0;
+   if (fh_cmp(name, "BITPIX")  >=MATCH_KEYWORD)	return 1.0;
+   if (fh_cmp(name, "NAXIS")   >=MATCH_SUBSTR) 	return 2.0 + 0.1 * atoi(name + 5);
+   if (fh_cmp(name, "EXTEND")  >=MATCH_KEYWORD)	return 3.0;
+   if (fh_cmp(name, "NEXTEND") >=MATCH_KEYWORD)	return 3.1;
+   if (fh_cmp(name, "GROUPS")  >=MATCH_KEYWORD)	return 4.0;
+   if (fh_cmp(name, "PCOUNT")  >=MATCH_KEYWORD)	return 5.0;
+   if (fh_cmp(name, "GCOUNT")  >=MATCH_KEYWORD)	return 6.0;
+   if (fh_cmp(name, "TFIELDS") >=MATCH_KEYWORD)	return 7.000;
+   if (fh_cmp(name, "TFORM")   >=MATCH_SUBSTR)  return 7.0001 + 0.001*atoi(name+5);
+   if (fh_cmp(name, "TBCOL")   >=MATCH_SUBSTR)  return 7.0002 + 0.001*atoi(name+5);
+   if (fh_cmp(name, "INHERIT") >=MATCH_KEYWORD) return 9.0;
+   if (fh_cmp(name, "END")     >=MATCH_SUBSTR)	return DBL_MAX;
+   return 10.0;
+}
+
+/*
+ * Find/Create a new card in the list.
+ */
+static char*
+get_card(HeaderUnitStruct* list, double idx, const char* name, double idxmatch)
+{
+   FitsCardPtr rtn;
+   int i;
+   double idx_auto = auto_idx(name);
+
+   if (idx == DBL_MAX)
+   {
+      log_warning("END is added automatically by libfh");
+      return 0;
+   }
+
+   /*
+    * If idx_auto < 10, the library should assign the idx value itself
+    * in such a way that the output will conform to the FITS standard.
+    */
+   if (idx_auto < 10.0) idx = idx_auto;
+   else
+      /*
+       * Otherwise, if the user's idx < 10.0, auto-increment the last idx.
+       */
+      if (idx < 10.0) idx = (list->idx_highest += IDX_AUTO_INCR);
+      else if (idx > list->idx_highest) list->idx_highest = idx;
+
+   /*
+    * Check if a card by the same name (or same name _and_ idxmatch,
+    * if idxmatch is non-zero) already existed.
+    */
+   if (name && (rtn = get_hdr(list, name, idxmatch))) return rtn->card;
+
+   /*
+    * Allocate more memory (just double the size) if table is full.
+    */
+   if (++list->len > list->size)
+   {
+      FitsCardPtr* newhdr;
+      if (!(newhdr = (FitsCardPtr*)realloc(list->hdr,
+					   sizeof(FitsCardPtr) *
+					   list->size * 2)))
+      {
+	 log_error("out of memory for FITS card slots");
+	 list->err_memory++;
+	 return 0; /* Save for delayed error from fh_rewrite() */
+	 /* Old list->hdr is still valid if realloc fails. */
+      }
+      list->size *= 2;
+      list->hdr = newhdr;
+   }
+
+   /*
+    * Allocate a new FITS card.
+    */
+   if (!(rtn = list->hdr[list->len-1] = (FitsCard*)malloc(sizeof(FitsCard))))
+   {
+      log_error("out of memory for new FITS card");
+      list->err_memory++; /* Save for delayed error from fh_rewrite() */
+      return 0;
+   }
+
+   /*
+    * Blank the entry and fill in the name.
+    */
+   rtn->idx = idx;
+   memset(rtn->card, ' ', FH_CARD_SIZE);
+   rtn->card[FH_CARD_SIZE] = '\0';
+
+   /*
+    * Check, and fill in the name if needed.
+    */
+   if (name)
+   {
+      for (i=0; i<FH_NAME_SIZE && *name; i++)
+	 rtn->card[i] = NAME_CHR(*name++);
+
+      /*
+       * The '=' case is OK.  It happens since fh_set_card passes
+       * the whole card in for name also.
+       */
+      if (*name!='\0' && *name!='=')
+	 log_warning("long (>8 byte) keyword name truncated");
+   }
+
+   return rtn->card;
+}
+
+
+static void
+add_comment(char* s, int col, const char* comment)
+{
+   /*
+    * Comments must not begin before column 30.
+    */
+   while (col < 30) s[col++] = ' ';
+
+   /*
+    * If no comment given, leave the old comment in place,
+    * if possible.  It could be that a longer string
+    * has overwritten the / that starts the comment, in which
+    * case this will make sure that the first non-space character
+    * after the value is still a '/'.
+    */
+   if (!comment || !*comment)
+   {
+      if (col < FH_CARD_SIZE) s[col++] = ' ';
+      while (col < FH_CARD_SIZE && s[col] == ' ') col++;
+      if (col < FH_CARD_SIZE) s[col++] = '/';
+      return;
+   }
+
+   if (col < 77 && comment && *comment)
+   {
+      s[col++] = ' ';
+      s[col++] = '/';
+      s[col++] = ' ';
+      while (col < FH_CARD_SIZE && *comment)
+      {
+	 if (*comment < ' ' || *comment >= 127)
+	 {
+	    s[col++] = '_';
+	    sprintf(err_buf, "illegal character %x in FITS card comment.",
+		    *comment++); /* Don't forget to increment comment */
+	    log_warning(err_buf);
+	 }
+	 else
+	 {
+	    s[col++] = *comment++;
+	 }
+      }
+   }
+   while (col < FH_CARD_SIZE) s[col++] = ' '; /* Blank out rest of old value, if any */
+}
+
+/* End of fh.c */
Index: /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfh/fh.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfh/fh.h	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfh/fh.h	(revision 23594)
@@ -0,0 +1,347 @@
+/*                                             -*- c-file-style: "Ellemtel" -*-
+
+`fh.h' - FITS Handling Routines for Standard FITS and MEF
+
+This file is part of version 1 of the FITS Handling Library.
+Read the `License' file for terms of use and distribution.
+Copyright 2001, Canada-France-Hawaii Telescope, daprog@cfht.hawaii.edu.
+
+___This header automatically generated from `Index'. Do not edit it here!___ */
+/* $Id: fh.h,v 1.5 2003/05/28 06:45:17 isani Exp isani $
+ * Created: 2001-4-2
+ * Documentation: See http://software.cfht.hawaii.edu/libfh/
+ */
+#ifndef _INCLUDED_fh
+#define _INCLUDED_fh 1
+/* ------------------------------------------------
+ * Definitions, Types, and Structures used by libfh
+ * ------------------------------------------------
+ */
+#define FH_MAX_STRLEN	68  	/* Longest possible FITS string value */
+#define FH_NAME_SIZE	8   	/* Size in bytes of a FITS card name */
+#define FH_CARD_SIZE	80  	/* Size in bytes of a FITS card */
+#define FH_BLOCK_SIZE	2880	/* FITS file block size in bytes (36 cards) */
+#define FH_AUTO		0.0 	/* Don't need `idx' numbers?  Use this everywhere. */
+#define FH_TYPESIZE_RAW	0 	/* Never swap the image data === Document! */
+#define FH_TYPESIZE_8	1 	/* 8-bit data (swapping not applicable) */
+#define FH_TYPESIZE_16	2 	/* 16-bit data, auto-swap if needed */
+#define FH_TYPESIZE_16U 3	/* 16-bit unsign, offset 32768 when writing */
+#define FH_TYPESIZE_16US -3	/* 16-bit unsign, offset 32768, force swap */
+#define FH_TYPESIZE_16URAW 5	/* 16-bit unsign, offset 32768, no swap */
+#define FH_TYPESIZE_16S	-2	/* 16-bit data, force swapping */
+#define FH_TYPESIZE_32	4 	/* 32-bit data, auto-swap if needed */
+#define FH_TYPESIZE_32S	-4	/* 32-bit data, force swapping */
+#define FH_TYPESIZE_64	8 	/* 64-bit data, auto-swap if needed */
+#define FH_TYPESIZE_64S	-8	/* 64-bit data, force swapping */
+#define FH_COUNT_CARDS "@FH_COUNT_CARDS@"  /* See also: fh_count_cards() */
+
+typedef enum /* fh_result -- Result Code for most functions in this library: */
+{  FH_SUCCESS = 0,
+   FH_IN_ERRNO = -1,	/* A system call failed; check value of errno */
+   FH_END_OF_FILE = -2,	/* EOF condition */
+   FH_NOT_FOUND = -3,	/* No such card or header unit found */
+   FH_INVALID = -4,	/* Invalid arguments passed (NULL pointer, etc.) */
+   FH_BAD_VALUE = -5,	/* Wrong type, error in conversion, string too long */
+   FH_BAD_SIZE = -6,	/* Size argument doesn't match BITPIX and NAXIS */
+   FH_BAD_PADDING = -7,	/* Problem with header padding or \0's after data */
+   FH_NO_SPACE = -8,	/* No space for header (file must be re-created.) */
+   FH_NO_MEMORY = -9,	/* A call to malloc failed somewhere */
+   FH_IS_TTY = -10,	/* This call doesn't work on tty devices */
+   FH_IS_DIR = -11,	/* This call requires a file, not a directory */
+   FH_INTERNAL_ERROR=-99, /* Internal error with the library */
+} fh_result;
+
+typedef enum
+{  FH_FILE_RDONLY,		/* Open, get non-exclusive lock, read header, unlock */
+   FH_FILE_RDWR,		/* Open, get exclusive lock, read, unlock on rewrite */
+   FH_FILE_RDONLY_NOLOCK,	/* Same as RDONLY but without advisory lock */
+   FH_FILE_RDWR_NOLOCK 		/* Same as RDWR but without advisory lock */
+} fh_mode;
+
+typedef enum
+{  FH_FALSE=0,			/* FITS card contains 'F' in column 30 */
+   FH_TRUE=1			/* FITS card contains 'T' in column 30 */
+} fh_bool;			/* Value for "logical" FITS cards */
+
+typedef void* HeaderUnit; /* Handle to a list of FITS cards allocated by fh_create. */
+
+typedef void (*fh_logger_t) (const char* message);
+void fh_log_error(fh_logger_t logger);	/* These are used only if you do not */
+void fh_log_perror(fh_logger_t logger);	/* want messages going to stderr. */
+void fh_log_warning(fh_logger_t logger); /* See manual for more information. */
+
+/* ---------------------------------------------------------
+ * Open, Close, Lock, or create new FITS files/header blocks
+ * ---------------------------------------------------------
+ */
+HeaderUnit	fh_create(void);
+fh_result	fh_destroy(HeaderUnit hu);
+/*
+ * These are the first and last functions called to allocate and free
+ * space for a HeaderUnit. After fh_create, use fh_file, fh_read, fh_set...
+ * NOT to be used with HeaderUnit's returned by the fh_ehu() functions!
+ */
+fh_result	fh_file(HeaderUnit hu, const char* filespec, fh_mode mode);
+/*
+ * Open file and read headers from the PHU or [extname] if given in filespec.
+ * File is left at start of image data in the file or 1st extension.
+ */
+int		fh_file_desc(HeaderUnit hu);
+/*
+ * Returns the file descriptor associated with HeaderUnit (or -1, if 
+ * if no file is open.)  This is only needed if you used fh_file() to
+ * open a file and you need to read or write its file descriptor directly.
+ */
+fh_result	fh_read(HeaderUnit hu, int fd, double idx);
+/*
+ * Alternative to fh_file() if you opened the file descriptor yourself.
+ * fh_read() reads cards from a file descriptor and inserts them into
+ * the HeaderUnit cards starting at "idx" (use FH_AUTO to append to the end.)
+ * `fd' is left at start of image data in the file or 1st extension.
+ */
+
+fh_result fh_read_keyword_buffer(HeaderUnit hu, const char* fitsblock, double idx, int fast);
+/*
+ * Use this function if keywords are already in memory instead of a file.
+ *
+ * Inputs:
+ *    fitsblock    - Exactly 2880 bytes of FITS keyword data in memory.
+ *    idx          - Starting sorting number (or use FH_AUTO).
+ *    fast         - If non-zero, skip all integrity checks in order to
+ *                   make parsing the keywords as fast as possible.
+ *
+ * Outputs:
+ *    hu           - Keyword header unit with parsed keywords added.
+ *
+ * Returns:
+ *    FH_INVALID   - If hu or fitsblock is NULL.
+ *    FH_NO_MEMORY - Memory allocation error
+ *    FH_BAD_VALUE - Illegal characters in keyword value (only if !fast)
+ *    FH_SUCCESS     - OK.
+ *    FH_END_OF_FILE - OK, and END Keyword found.
+ */
+
+fh_result fh_count_cards(HeaderUnit hu);
+/*
+ * Any following fh_[re]write() call will print the number of cards
+ * needed instead of actually updating a file.  This mode is also
+ * selected when fh_file() is called with filedesc = FH_COUNT_CARDS.
+ */
+fh_result fh_reserve(HeaderUnit hu, int n);
+/*
+ * Set the number of COMMENT cards to reserve in the output when fh_write()
+ * is used to create a new FITS header.  NOT for use with fh_rewrite().
+ */
+fh_result fh_validate(HeaderUnit hu); /* fh_validate.c; for use by fhtool.c */
+
+fh_result fh_write(HeaderUnit hu, int fd);
+/*
+ * Writes cards in 2880 byte **PADDED** blocks to a file descriptor.
+ * This adds `END' line also, and file pointer `fd' is left at start of
+ * image data, just like it is after a call to fh_file().
+ */
+
+fh_result fh_write_keyword_buffer(HeaderUnit hu, char* buffer, int* fits_blocks_inout);
+/*
+ * Use this function to get formatted keywords into a memory buffer.
+ *
+ * Inputs:
+ *    hu                - Header unit structure with some keywords in it.
+ *    buffer            - Pointer to user allocated memory buffer
+ *    fits_blocks_inout - Pointer to buffer size / 2880 (FH_BLOCK_SIZE)
+ *
+ * Outputs:
+ *    fits_blocks_inout - Returns with the number of 2880-byte blocks copied.
+ *
+ * Returns:
+ *    FH_INVALID   - An argument was null or invalid.
+ *    FH_NO_SPACE  - Partial header copied, but there was insufficient space.
+ *    FH_NO_MEMORY - Memory allocation error occurred previously (on fh_set)
+ *    FH_SUCCESS   - OK.
+ */
+
+fh_result fh_map_raw_image(HeaderUnit hu, void** data, int size);
+fh_result fh_munmap_raw_image(HeaderUnit hu);
+/*
+ * These calls give the calling program access to a memory mapped pointer
+ * to the data in the FITS file.  The data format is equivalent to what
+ * would be produced by fh_read_image() with typesize=FH_TYPESIZE_RAW.
+ * Whether or not the file can be modified through the memory-mapped
+ * pointer depends on whether fh_mode=FH_FILE_RDONLY or FH_FILE_RDWR
+ * in the call to fh_file() that opened the file.  Be sure to use
+ * fh_munmap_raw_image() when done.
+ *
+ * `size' must match the exact number bytes of data which should be in
+ * the file, according to its NAXIS and BITPIX values, excluding padding.
+ *
+ * %%% TODO: FH_FILE_RDWR is completely unimplemented!
+ *
+ * %%% TODO: File locking is not handled properly yet.  For now, use
+ * FH_FILE_RDONLY_NOLOCK and FH_FILE_RDWR_NOLOCK, especially on MEF
+ * files where the locks are left in place until the file is closed
+ * otherwise!
+ */
+
+fh_result fh_write_image(HeaderUnit hu, int fd, void* data, int size, int typesize);
+fh_result fh_write_padding(HeaderUnit hu, int fd);
+fh_result fh_write_padded_image(HeaderUnit hu, int fd, void* data, int size, int typesize);
+/*
+ * fh_write_image and fh_write_padded_image writes exactly `size' bytes of
+ * `data' at the current file position of `fd'.  In the case of
+ * fh_write_padded_image, it is followed by enough padding to reach an
+ * FH_BLOCK_SIZE boundary, and `size' must match what the library
+ * calculates from the NAXIS and BITPIX values in `hu', excluding padding.
+ *
+ * fh_write_padding writes padding meant to go AFTER THE DATA.  (The header
+ * is automatically padded.)  This routine is used if you write the data
+ * to fd yourself, or in segments using fh_write_image().
+ */
+fh_result fh_read_image(HeaderUnit hu, int fd, void* data, int size, int typesize);
+fh_result fh_read_padding(HeaderUnit hu, int fd);
+fh_result fh_read_padded_image(HeaderUnit hu, int fd, void* data, int size, int typesize);
+/*
+ * Read image data into buffer.  Padding is not copied into *data.
+ * To read data in segments, use fh_read_image() followed by a final
+ * call to fh_read_padding().  Calling fh_read_padding() and ensuring
+ * that it returns FH_SUCCESS helps ensure that the entire file was
+ * read correctly.
+ *
+ * Use the final form, fh_read_padded_image() to read the entire data
+ * in one call.  In this case, as with fh_write_padded_image(), `size'
+ * must match the exact number bytes of data which should be in the
+ * file, according to its NAXIS and BITPIX values, excluding padding.
+ */
+fh_result fh_copy_padded_image(HeaderUnit hu, int fd_out, int fd_in);
+/*
+ * Read image data for `fd_out' from `fd_in'.
+ */
+fh_result fh_copy_and_verify_padded_image(HeaderUnit hu, int fd_out, int fd_in);
+/*
+ * Just like fh_copy_padded_image, but with an additional check for blocks
+ * of data with zero pixels.
+ */
+
+/* WARNING: If you intend to make a utility that can read from pipes
+ * (stdin) then you should avoid the following functions.  The are the
+ * only routines which need to be able to seek in a file.  If you are
+ * always reading from a file, this routines are ok.
+ */
+/* ------------- BEGIN ROUTINES WHICH USE lseek() ....  ------------ */
+fh_result fh_reserve_padded_image(HeaderUnit hu, int fd);
+/*
+ * This is like fh_write_padded_image(), except that it quickly seeks
+ * to the location where the last byte of actual image data will
+ * eventually go, writes a NUL character, and then writes all of the
+ * padding.  This sets things up for a future write by mmap.
+ */
+fh_result fh_rewrite(HeaderUnit hu);
+/*
+ * This call is only valid if fh_read() has been called to create `hu' in the
+ * first place.  Headers are written back to the same file from which they
+ * were read.  Your program should check for FH_NO_SPACE (and any other non-
+ * FH_SUCCESS returns).  FH_NO_SPACE means there was not enough room to
+ * rewrite the modified header unit back to the same file.
+ */
+void fh_link_ehu_to_phu(HeaderUnit ehu, HeaderUnit phu);
+
+HeaderUnit fh_ehu(HeaderUnit phu, int number);
+HeaderUnit fh_ehu_by_imageid(HeaderUnit phu, int imageid);
+HeaderUnit fh_ehu_by_extname(HeaderUnit phu, const char* extname);
+/*
+ * `phu' must be first be filled from a file by fh_read().
+ * `number' = 0 returns the phu and seeks the file to end of the phu cards.
+ * `number' = 1,2,3 returns the extension 1,2,3 and seeks to the start of data.
+ * Last two functions search for matching IMAGEID or EXTNAME cards.
+ */
+/* ------------- END OF ROUTINES WHICH USE lseek() ...  ------------ */
+
+int fh_extensions(HeaderUnit hu); /* Count extensions (0 if EXTEND=F) */
+
+int fh_image_bytes(HeaderUnit hu); /* Proper # of image bytes for hu */
+int fh_image_blocks(HeaderUnit hu); /* Same value, div 2880, rounded up */
+int fh_header_blocks(HeaderUnit hu); /* # of header bytes, div 2880, rnd. up */
+/*
+ * The number of image_blocks is calculated from hu's BITPIX and NAXIS cards.
+ * The image_blocks functions return 0 if BITPIX or an NAXIS card is missing.
+ */
+/* ----------------------
+ * Getting Keyword Values
+ * ----------------------
+ */
+fh_result fh_get_bool(HeaderUnit hu, const char* name, fh_bool* value);
+fh_result fh_get_int(HeaderUnit hu, const char* name, int* value);
+fh_result fh_get_flt(HeaderUnit hu, const char* name, double* value);
+fh_result fh_get_str(HeaderUnit hu, const char* name, char* value, int maxlen);
+/*
+ * Get FITS card values from `hu'.
+ * For fh_get_str(), `maxlen' is the size of the buffer, including
+ * space for a '\0' termination character which will be added by
+ * this routine.  If it doesn't fit, a FH_BAD_VALUE will be returned.
+ * (Pass buffers of FH_MAX_STRLEN + 1 to be sure this doesn't happen.)
+ */
+double fh_idx_after(HeaderUnit hu, const char* name);
+double fh_idx_before(HeaderUnit hu, const char* name);
+/*
+ * Get a value for a new `idx' which will cause a new card to be inserted
+ * just after (or before) an existing card `name'.  If `name' does not
+ * exist, this function returns FH_AUTO, which causes the new card to be
+ * added to the end of the header.
+ */
+fh_result fh_search(HeaderUnit hu, const char* name, double* idx);
+/*
+ * Search for a particular card, and (if idx!=0), save a copy of its
+ * internal sorting number.  The `idx' is useful if you need to insert
+ * cards just before or after those read from a file.  (Increment or
+ * decrement the idx value by .000001)
+ */
+fh_result fh_show(HeaderUnit hu); /* Print `hu' to screen for debugging */
+
+/* ----------------------
+ * Setting Keyword Values
+ * ----------------------
+ */
+fh_result fh_remove(HeaderUnit hu, const char* name);
+/*
+ * Removes a card `name' from `hu'.  COMMENTS cannot be removed.
+ */
+fh_result fh_set_all_units(HeaderUnit hu);
+/*
+ * By default, if fh_set*() operations are performed on a PHU read
+ * from a file, they are not applied to extension units.  Call this
+ * function BEFORE calling any other fh_set* functions on a PHU when
+ * you also want the extensions (which you'd get with fh_ehu()) to
+ * be changed, otherwise fh_set() only applies to the current HeaderUnit.
+ */
+void fh_set_str(HeaderUnit hu, double idx, const char* name,
+		const char* value, const char* comment);
+void fh_set_com(HeaderUnit hu, double idx, const char* name,
+		const char* comment);
+void fh_set_int(HeaderUnit hu, double idx, const char* name,
+		int value, const char* comment);
+void fh_set_flt(HeaderUnit hu, double idx, const char* name,
+		double value, int significant_digits, const char* comment);
+void fh_set_pfl(HeaderUnit hu, double idx, const char* name,
+		double value, int decimal_places, const char* comments);
+void fh_set_bool(HeaderUnit hu, double idx, const char* name,
+		 fh_bool value, const char* comment);
+/*
+ * Change/Create FITS card in table of type string, integer, or floating point.
+ * The comment field may get silently truncated if it doesn't fit in 80 cols.
+ */
+void fh_set_val(HeaderUnit hu, double idx, const char* name,
+		const char* value, const char* comment);
+/*
+ * This final form of fh_set is used by neodatah to insert pre-formatted
+ * FITS cards (80 bytes) obtained from the status server staging area.
+ */
+void fh_set_card(HeaderUnit hu, double idx, const char* card);
+/*
+ * This one is used by the nheader command in DetCom to set a value with
+ * "auto-detected" type of float, int, boolean, or string.  Use typed
+ * functions instead, when the value type is known.
+ */
+const char* fh_first(HeaderUnit hu);	/* See examples.  These are */
+const char* fh_next(HeaderUnit hu);	/* used to iterate through a list */
+double fh_idx(HeaderUnit hu); /* idx of the last card returned by fh_next */
+fh_result fh_merge(HeaderUnit hu, const HeaderUnit source); /* source unchanged */
+#endif /* _INCLUDED_fh */
Index: /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfh/fh_registry.asm
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfh/fh_registry.asm	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfh/fh_registry.asm	(revision 23594)
@@ -0,0 +1,786 @@
+DETCOM_SIMPLE MACRO ARG
+    COBJ "detcom:dheader 0.0 SIMPLE \"\ARG\" \"Standard FITS\""
+    ENDM
+DETCOM_XTENSION MACRO ARG
+    COBJ "detcom:dheader 0.0 XTENSION \"\ARG\" \"\""
+    ENDM
+DETCOM_BITPIX MACRO ARG
+    COBJ "detcom:dheader 1.0 BITPIX \"\ARG\" \"Bits per pixel\""
+    ENDM
+DETCOM_NAXIS MACRO ARG
+    COBJ "detcom:dheader 2.0 NAXIS \"\ARG\" \"Number of axes\""
+    ENDM
+DETCOM_NAXIS1 MACRO ARG
+    COBJ "detcom:dheader 2.1 NAXIS1 \"\ARG\" \"Number of pixel columns\""
+    ENDM
+DETCOM_NAXIS2 MACRO ARG
+    COBJ "detcom:dheader 2.2 NAXIS2 \"\ARG\" \"Number of pixel rows\""
+    ENDM
+DETCOM_NAXIS3 MACRO ARG
+    COBJ "detcom:dheader 2.3 NAXIS3 \"\ARG\" \"Number of stacked frames (cube)\""
+    ENDM
+DETCOM_EXTEND MACRO ARG
+    COBJ "detcom:dheader 3.0 EXTEND \"\ARG\" \"File contains extensions\""
+    ENDM
+DETCOM_NEXTEND MACRO ARG
+    COBJ "detcom:dheader 3.1 NEXTEND \"\ARG\" \"Number of extensions\""
+    ENDM
+DETCOM_GROUPS MACRO ARG
+    COBJ "detcom:dheader 4.0 GROUPS \"\ARG\" \"File contains random groups records\""
+    ENDM
+DETCOM_PCOUNT MACRO ARG
+    COBJ "detcom:dheader 5.0 PCOUNT \"\ARG\" \"Random parameters before each array in a group\""
+    ENDM
+DETCOM_GCOUNT MACRO ARG
+    COBJ "detcom:dheader 6.0 GCOUNT \"\ARG\" \"Number of random groups\""
+    ENDM
+DETCOM_TFIELDS MACRO ARG
+    COBJ "detcom:dheader 7.0000 TFIELDS \"\ARG\" \"Number of fields in a row\""
+    ENDM
+DETCOM_TFORM1 MACRO ARG
+    COBJ "detcom:dheader 7.0011 TFORM1 \"\ARG\" \"Table format for field 1\""
+    ENDM
+DETCOM_TBCOL1 MACRO ARG
+    COBJ "detcom:dheader 7.0012 TBCOL1 \"\ARG\" \"Start Column for field 1\""
+    ENDM
+DETCOM_TFORM2 MACRO ARG
+    COBJ "detcom:dheader 7.0021 TFORM2 \"\ARG\" \"Table format for field 2\""
+    ENDM
+DETCOM_TBCOL2 MACRO ARG
+    COBJ "detcom:dheader 7.0022 TBCOL2 \"\ARG\" \"Start Column for field 2\""
+    ENDM
+DETCOM_TFORM3 MACRO ARG
+    COBJ "detcom:dheader 7.0031 TFORM3 \"\ARG\" \"Table format for field 3\""
+    ENDM
+DETCOM_TBCOL3 MACRO ARG
+    COBJ "detcom:dheader 7.0032 TBCOL3 \"\ARG\" \"Start Column for field 3\""
+    ENDM
+DETCOM_TFORM4 MACRO ARG
+    COBJ "detcom:dheader 7.0041 TFORM4 \"\ARG\" \"Table format for field 4\""
+    ENDM
+DETCOM_TBCOL4 MACRO ARG
+    COBJ "detcom:dheader 7.0042 TBCOL4 \"\ARG\" \"Start Column for field 4\""
+    ENDM
+DETCOM_TFORM5 MACRO ARG
+    COBJ "detcom:dheader 7.0051 TFORM5 \"\ARG\" \"Table format for field 5\""
+    ENDM
+DETCOM_TBCOL5 MACRO ARG
+    COBJ "detcom:dheader 7.0052 TBCOL5 \"\ARG\" \"Start Column for field 5\""
+    ENDM
+DETCOM_CMMTOBS MACRO ARG
+    COBJ "detcom:dheader 51.0 CMMTOBS \"\ARG\" \"\""
+    ENDM
+DETCOM_CMMTSEQ MACRO ARG
+    COBJ "detcom:dheader 52.0 CMMTSEQ \"\ARG\" \"\""
+    ENDM
+DETCOM_OBJECT MACRO ARG
+    COBJ "detcom:dheader 53.0 OBJECT \"\ARG\" \"\""
+    ENDM
+DETCOM_OBSERVER MACRO ARG
+    COBJ "detcom:dheader 54.0 OBSERVER \"\ARG\" \"\""
+    ENDM
+DETCOM_PI_NAME MACRO ARG
+    COBJ "detcom:dheader 55.0 PI_NAME \"\ARG\" \"\""
+    ENDM
+DETCOM_RUNID MACRO ARG
+    COBJ "detcom:dheader 56.0 RUNID \"\ARG\" \"\""
+    ENDM
+DETCOM_FILENAME MACRO ARG
+    COBJ "detcom:dheader 71.00 FILENAME \"\ARG\" \"Base filename at acquisition\""
+    ENDM
+DETCOM_PATHNAME MACRO ARG
+    COBJ "detcom:dheader 71.01 PATHNAME \"\ARG\" \"Original directory name at acquisition\""
+    ENDM
+DETCOM_EXTNAME MACRO ARG
+    COBJ "detcom:dheader 71.10 EXTNAME \"\ARG\" \"Extension name\""
+    ENDM
+DETCOM_EXTVER MACRO ARG
+    COBJ "detcom:dheader 71.20 EXTVER \"\ARG\" \"Extension version\""
+    ENDM
+DETCOM_DATE MACRO ARG
+    COBJ "detcom:dheader 74.00 DATE \"\ARG\" \"UTC Date of file creation\""
+    ENDM
+DETCOM_HSTTIME MACRO ARG
+    COBJ "detcom:dheader 74.10 HSTTIME \"\ARG\" \"Local time in Hawaii\""
+    ENDM
+DETCOM_IMAGESWV MACRO ARG
+    COBJ "detcom:dheader 77.00 IMAGESWV \"\ARG\" \"Image creation software version\""
+    ENDM
+DETCOM_DETECTOR MACRO ARG
+    COBJ "detcom:dheader 91.0 DETECTOR \"\ARG\" \"Science Detector\""
+    ENDM
+DETCOM_INSTRUME MACRO ARG
+    COBJ "detcom:dheader 92.0 INSTRUME \"\ARG\" \"Instrument Name\""
+    ENDM
+DETCOM_INSTMODE MACRO ARG
+    COBJ "detcom:dheader 92.1 INSTMODE \"\ARG\" \"Instrument Mode\""
+    ENDM
+DETCOM_DETSIZE MACRO ARG
+    COBJ "detcom:dheader 102.0 DETSIZE \"\ARG\" \"Total data pixels in full mosaic\""
+    ENDM
+DETCOM_RASTER MACRO ARG
+    COBJ "detcom:dheader 103.0 RASTER \"\ARG\" \"\""
+    ENDM
+DETCOM_CCDSUM MACRO ARG
+    COBJ "detcom:dheader 105.0 CCDSUM \"\ARG\" \"Binning factors\""
+    ENDM
+DETCOM_CCDBIN1 MACRO ARG
+    COBJ "detcom:dheader 105.1 CCDBIN1 \"\ARG\" \"Binning factor along first axis\""
+    ENDM
+DETCOM_CCDBIN2 MACRO ARG
+    COBJ "detcom:dheader 105.2 CCDBIN2 \"\ARG\" \"Binning factor along second axis\""
+    ENDM
+DETCOM_PIXSIZE MACRO ARG
+    COBJ "detcom:dheader 110.0 PIXSIZE \"\ARG\" \"Pixel size for both axes (microns)\""
+    ENDM
+DETCOM_PIXSIZE1 MACRO ARG
+    COBJ "detcom:dheader 111.1 PIXSIZE1 \"\ARG\" \"Pixel size for axis 1 (microns)\""
+    ENDM
+DETCOM_PIXSIZE2 MACRO ARG
+    COBJ "detcom:dheader 111.2 PIXSIZE2 \"\ARG\" \"Pixel size for axis 2 (microns)\""
+    ENDM
+DETCOM_PIXSCAL1 MACRO ARG
+    COBJ "detcom:dheader 112.1 PIXSCAL1 \"\ARG\" \"Pixel scale for axis 1 (arcsec/pixel)\""
+    ENDM
+DETCOM_PIXSCAL2 MACRO ARG
+    COBJ "detcom:dheader 112.2 PIXSCAL2 \"\ARG\" \"Pixel scale for axis 2 (arcsec/pixel)\""
+    ENDM
+DETCOM_AMPLIST MACRO ARG
+    COBJ "detcom:dheader 130.0 AMPLIST \"\ARG\" \"List of amplifiers for this image\""
+    ENDM
+DETCOM_AMPNAME MACRO ARG
+    COBJ "detcom:dheader 131.0 AMPNAME \"\ARG\" \"Amplifier name\""
+    ENDM
+DETCOM_CCDSIZE MACRO ARG
+    COBJ "detcom:dheader 140.0 CCDSIZE \"\ARG\" \"Detector imaging area size\""
+    ENDM
+DETCOM_DETSEC MACRO ARG
+    COBJ "detcom:dheader 140.1 DETSEC \"\ARG\" \"Mosaic area of the detector\""
+    ENDM
+DETCOM_DETSECA MACRO ARG
+    COBJ "detcom:dheader 140.11 DETSECA \"\ARG\" \"Mosaic area of the detector from Amp A\""
+    ENDM
+DETCOM_DETSECB MACRO ARG
+    COBJ "detcom:dheader 140.12 DETSECB \"\ARG\" \"Mosaic area of the detector from Amp B\""
+    ENDM
+DETCOM_DETSECC MACRO ARG
+    COBJ "detcom:dheader 140.13 DETSECC \"\ARG\" \"Mosaic area of the detector from Amp C\""
+    ENDM
+DETCOM_DETSECD MACRO ARG
+    COBJ "detcom:dheader 140.14 DETSECD \"\ARG\" \"Mosaic area of the detector from Amp D\""
+    ENDM
+DETCOM_DATASEC MACRO ARG
+    COBJ "detcom:dheader 140.2 DATASEC \"\ARG\" \"Imaging area of the detector\""
+    ENDM
+DETCOM_BIASSEC MACRO ARG
+    COBJ "detcom:dheader 140.3 BIASSEC \"\ARG\" \"Overscan (bias) area of the detector\""
+    ENDM
+DETCOM_ASECA MACRO ARG
+    COBJ "detcom:dheader 141.11 ASECA \"\ARG\" \"Section from Amp A (non-contig. bias excluded)\""
+    ENDM
+DETCOM_ASECB MACRO ARG
+    COBJ "detcom:dheader 141.12 ASECB \"\ARG\" \"Section from Amp B (non-contig. bias excluded)\""
+    ENDM
+DETCOM_ASECC MACRO ARG
+    COBJ "detcom:dheader 141.13 ASECC \"\ARG\" \"Section from Amp C (non-contig. bias excluded)\""
+    ENDM
+DETCOM_ASECD MACRO ARG
+    COBJ "detcom:dheader 141.14 ASECD \"\ARG\" \"Section from Amp D (non-contig. bias excluded)\""
+    ENDM
+DETCOM_BSECA MACRO ARG
+    COBJ "detcom:dheader 141.21 BSECA \"\ARG\" \"Overscan (bias) area from Amp A\""
+    ENDM
+DETCOM_BSECB MACRO ARG
+    COBJ "detcom:dheader 141.22 BSECB \"\ARG\" \"Overscan (bias) area from Amp B\""
+    ENDM
+DETCOM_BSECC MACRO ARG
+    COBJ "detcom:dheader 141.23 BSECC \"\ARG\" \"Overscan (bias) area from Amp C\""
+    ENDM
+DETCOM_BSECD MACRO ARG
+    COBJ "detcom:dheader 141.24 BSECD \"\ARG\" \"Overscan (bias) area from Amp D\""
+    ENDM
+DETCOM_CSECA MACRO ARG
+    COBJ "detcom:dheader 141.31 CSECA \"\ARG\" \"Section in full CCD for DSECA\""
+    ENDM
+DETCOM_CSECB MACRO ARG
+    COBJ "detcom:dheader 141.32 CSECB \"\ARG\" \"Section in full CCD for DSECB\""
+    ENDM
+DETCOM_CSECC MACRO ARG
+    COBJ "detcom:dheader 141.33 CSECC \"\ARG\" \"Section in full CCD for DSECC\""
+    ENDM
+DETCOM_CSECD MACRO ARG
+    COBJ "detcom:dheader 141.34 CSECD \"\ARG\" \"Section in full CCD for DSECD\""
+    ENDM
+DETCOM_DSECA MACRO ARG
+    COBJ "detcom:dheader 141.41 DSECA \"\ARG\" \"Imaging area from Amp A\""
+    ENDM
+DETCOM_DSECB MACRO ARG
+    COBJ "detcom:dheader 141.42 DSECB \"\ARG\" \"Imaging area from Amp B\""
+    ENDM
+DETCOM_DSECC MACRO ARG
+    COBJ "detcom:dheader 141.43 DSECC \"\ARG\" \"Imaging area from Amp C\""
+    ENDM
+DETCOM_DSECD MACRO ARG
+    COBJ "detcom:dheader 141.44 DSECD \"\ARG\" \"Imaging area from Amp D\""
+    ENDM
+DETCOM_TSECA MACRO ARG
+    COBJ "detcom:dheader 141.51 TSECA \"\ARG\" \"Trim section for Amp A\""
+    ENDM
+DETCOM_TSECB MACRO ARG
+    COBJ "detcom:dheader 141.52 TSECB \"\ARG\" \"Trim section for Amp B\""
+    ENDM
+DETCOM_TSECC MACRO ARG
+    COBJ "detcom:dheader 141.53 TSECC \"\ARG\" \"Trim section for Amp C\""
+    ENDM
+DETCOM_TSECD MACRO ARG
+    COBJ "detcom:dheader 141.54 TSECD \"\ARG\" \"Trim section for Amp D\""
+    ENDM
+DETCOM_CCDNAME MACRO ARG
+    COBJ "detcom:dheader 150.1 CCDNAME \"\ARG\" \"Name of the CCD (manufacturer reference)\""
+    ENDM
+DETCOM_CCDNICK MACRO ARG
+    COBJ "detcom:dheader 150.2 CCDNICK \"\ARG\" \"Nickname of the CCD\""
+    ENDM
+DETCOM_MAXLIN MACRO ARG
+    COBJ "detcom:dheader 152.0 MAXLIN \"\ARG\" \"Maximum linearity value (ADU)\""
+    ENDM
+DETCOM_MAXLINA MACRO ARG
+    COBJ "detcom:dheader 152.01 MAXLINA \"\ARG\" \"Maximum linearity value for Amp A (ADU)\""
+    ENDM
+DETCOM_MAXLINB MACRO ARG
+    COBJ "detcom:dheader 152.02 MAXLINB \"\ARG\" \"Maximum linearity value for Amp B (ADU)\""
+    ENDM
+DETCOM_MAXLINC MACRO ARG
+    COBJ "detcom:dheader 152.03 MAXLINC \"\ARG\" \"Maximum linearity value for Amp C (ADU)\""
+    ENDM
+DETCOM_MAXLIND MACRO ARG
+    COBJ "detcom:dheader 152.04 MAXLIND \"\ARG\" \"Maximum linearity value for Amp D (ADU)\""
+    ENDM
+DETCOM_SATURATE MACRO ARG
+    COBJ "detcom:dheader 152.1 SATURATE \"\ARG\" \"Saturation value (ADU)\""
+    ENDM
+DETCOM_GAIN MACRO ARG
+    COBJ "detcom:dheader 155.0 GAIN \"\ARG\" \"Amplifier gain (electrons/ADU)\""
+    ENDM
+DETCOM_GAINA MACRO ARG
+    COBJ "detcom:dheader 155.01 GAINA \"\ARG\" \"Amp A gain (electrons/ADU)\""
+    ENDM
+DETCOM_GAINB MACRO ARG
+    COBJ "detcom:dheader 155.02 GAINB \"\ARG\" \"Amp B gain (electrons/ADU)\""
+    ENDM
+DETCOM_GAINC MACRO ARG
+    COBJ "detcom:dheader 155.03 GAINC \"\ARG\" \"Amp C gain (electrons/ADU)\""
+    ENDM
+DETCOM_GAIND MACRO ARG
+    COBJ "detcom:dheader 155.04 GAIND \"\ARG\" \"Amp D gain (electrons/ADU)\""
+    ENDM
+DETCOM_RDNOISE MACRO ARG
+    COBJ "detcom:dheader 156.0 RDNOISE \"\ARG\" \"Read noise (electrons)\""
+    ENDM
+DETCOM_RDNOISEA MACRO ARG
+    COBJ "detcom:dheader 156.01 RDNOISEA \"\ARG\" \"Amp A read noise (electrons)\""
+    ENDM
+DETCOM_RDNOISEB MACRO ARG
+    COBJ "detcom:dheader 156.02 RDNOISEB \"\ARG\" \"Amp B read noise (electrons)\""
+    ENDM
+DETCOM_RDNOISEC MACRO ARG
+    COBJ "detcom:dheader 156.03 RDNOISEC \"\ARG\" \"Amp C read noise (electrons)\""
+    ENDM
+DETCOM_RDNOISED MACRO ARG
+    COBJ "detcom:dheader 156.04 RDNOISED \"\ARG\" \"Amp D read noise (electrons)\""
+    ENDM
+DETCOM_DCURRENT MACRO ARG
+    COBJ "detcom:dheader 157.0 DCURRENT \"\ARG\" \"Dark current (ADU/pixel/second)\""
+    ENDM
+DETCOM_DARKCUR MACRO ARG
+    COBJ "detcom:dheader 157.1 DARKCUR \"\ARG\" \"Dark current (e-/pixel/hour)\""
+    ENDM
+DETCOM_QEPOINTS MACRO ARG
+    COBJ "detcom:dheader 158.0 QEPOINTS \"\ARG\" \"QE%@wavelength in nm\""
+    ENDM
+DETCOM_CONSWV MACRO ARG
+    COBJ "detcom:dheader 170.0 CONSWV \"\ARG\" \"Controller software DSPID and SERNO versions\""
+    ENDM
+DETCOM_DETSTAT MACRO ARG
+    COBJ "detcom:dheader 180.0 DETSTAT \"\ARG\" \"Detector status\""
+    ENDM
+DETCOM_DETTEM MACRO ARG
+    COBJ "detcom:dheader 190.0 DETTEM \"\ARG\" \"Detector temperature\""
+    ENDM
+DETCOM_TELESCOP MACRO ARG
+    COBJ "detcom:dheader 501.1 TELESCOP \"\ARG\" \"\""
+    ENDM
+DETCOM_ORIGIN MACRO ARG
+    COBJ "detcom:dheader 501.2 ORIGIN \"\ARG\" \"Canada-France-Hawaii Telescope\""
+    ENDM
+DETCOM_TELSTAT MACRO ARG
+    COBJ "detcom:dheader 503.0 TELSTAT \"\ARG\" \"Telescope Control System status\""
+    ENDM
+DETCOM_TIMESYS MACRO ARG
+    COBJ "detcom:dheader 510.0 TIMESYS \"\ARG\" \"Time System for DATExxxx\""
+    ENDM
+DETCOM_UTIME MACRO ARG
+    COBJ "detcom:dheader 512.0 UTIME \"\ARG\" \"Time at start of observation (UT)\""
+    ENDM
+DETCOM_SIDTIME MACRO ARG
+    COBJ "detcom:dheader 514.0 SIDTIME \"\ARG\" \"Sidereal time at start of observation\""
+    ENDM
+DETCOM_EPOCH MACRO ARG
+    COBJ "detcom:dheader 521.0 EPOCH \"\ARG\" \"Equinox of coordinates\""
+    ENDM
+DETCOM_EQUINOX MACRO ARG
+    COBJ "detcom:dheader 521.1 EQUINOX \"\ARG\" \"Equinox of coordinates\""
+    ENDM
+DETCOM_RADECSYS MACRO ARG
+    COBJ "detcom:dheader 522.0 RADECSYS \"\ARG\" \"Coordinate system for equinox (FK4/FK5/GAPPT)\""
+    ENDM
+DETCOM_RA MACRO ARG
+    COBJ "detcom:dheader 522.1 RA \"\ARG\" \"Object right ascension\""
+    ENDM
+DETCOM_DEC MACRO ARG
+    COBJ "detcom:dheader 522.2 DEC \"\ARG\" \"Object declination\""
+    ENDM
+DETCOM_CTYPE1 MACRO ARG
+    COBJ "detcom:dheader 525.211 CTYPE1 \"\ARG\" \"WCS Coordinate type\""
+    ENDM
+DETCOM_CTYPE2 MACRO ARG
+    COBJ "detcom:dheader 525.212 CTYPE2 \"\ARG\" \"WCS Coordinate type\""
+    ENDM
+DETCOM_CD1_1 MACRO ARG
+    COBJ "detcom:dheader 525.411 CD1_1 \"\ARG\" \"WCS Coordinate scale matrix\""
+    ENDM
+DETCOM_CD1_2 MACRO ARG
+    COBJ "detcom:dheader 525.412 CD1_2 \"\ARG\" \"WCS Coordinate scale matrix\""
+    ENDM
+DETCOM_CD2_1 MACRO ARG
+    COBJ "detcom:dheader 525.421 CD2_1 \"\ARG\" \"WCS Coordinate scale matrix\""
+    ENDM
+DETCOM_CD2_2 MACRO ARG
+    COBJ "detcom:dheader 525.422 CD2_2 \"\ARG\" \"WCS Coordinate scale matrix\""
+    ENDM
+DETCOM_NGUIDER MACRO ARG
+    COBJ "detcom:dheader 530.0 NGUIDER \"\ARG\" \"TCS Number of guiders\""
+    ENDM
+DETCOM_NGUISTAR MACRO ARG
+    COBJ "detcom:dheader 530.01 NGUISTAR \"\ARG\" \"TCS number of guide stars/probes in use\""
+    ENDM
+DETCOM_GUINAME MACRO ARG
+    COBJ "detcom:dheader 530.1 GUINAME \"\ARG\" \"TCS guider name\""
+    ENDM
+DETCOM_GUIEQUIN MACRO ARG
+    COBJ "detcom:dheader 530.21 GUIEQUIN \"\ARG\" \"TCS guider equinox\""
+    ENDM
+DETCOM_GUIRADEC MACRO ARG
+    COBJ "detcom:dheader 530.22 GUIRADEC \"\ARG\" \"TCS guider system for equinox\""
+    ENDM
+DETCOM_GUIRA MACRO ARG
+    COBJ "detcom:dheader 530.23 GUIRA \"\ARG\" \"TCS guider right ascension\""
+    ENDM
+DETCOM_GUIDEC MACRO ARG
+    COBJ "detcom:dheader 530.24 GUIDEC \"\ARG\" \"TCS guider declination\""
+    ENDM
+DETCOM_GUIOBJN MACRO ARG
+    COBJ "detcom:dheader 530.3 GUIOBJN \"\ARG\" \"TCS guider object name\""
+    ENDM
+DETCOM_GUIFLUX MACRO ARG
+    COBJ "detcom:dheader 530.6 GUIFLUX \"\ARG\" \"TCS guider flux\""
+    ENDM
+DETCOM_SKYFLUX MACRO ARG
+    COBJ "detcom:dheader 530.8 SKYFLUX \"\ARG\" \"TCS total sky flux\""
+    ENDM
+DETCOM_GUINAME1 MACRO ARG
+    COBJ "detcom:dheader 531.1 GUINAME1 \"\ARG\" \"TCS guider #1 identification\""
+    ENDM
+DETCOM_GUIEQUI1 MACRO ARG
+    COBJ "detcom:dheader 531.21 GUIEQUI1 \"\ARG\" \"TCS guider #1 equinox\""
+    ENDM
+DETCOM_GUIRADE1 MACRO ARG
+    COBJ "detcom:dheader 531.22 GUIRADE1 \"\ARG\" \"TCS guider #1 system for equinox\""
+    ENDM
+DETCOM_GUIRA1 MACRO ARG
+    COBJ "detcom:dheader 531.23 GUIRA1 \"\ARG\" \"TCS guider #1 right ascension\""
+    ENDM
+DETCOM_GUIDEC1 MACRO ARG
+    COBJ "detcom:dheader 531.24 GUIDEC1 \"\ARG\" \"TCS guider #1 declination\""
+    ENDM
+DETCOM_GUIOBJN1 MACRO ARG
+    COBJ "detcom:dheader 531.3 GUIOBJN1 \"\ARG\" \"TCS guider #1 object name\""
+    ENDM
+DETCOM_GUIFLUX1 MACRO ARG
+    COBJ "detcom:dheader 531.6 GUIFLUX1 \"\ARG\" \"TCS guider #1 flux\""
+    ENDM
+DETCOM_GUINAME2 MACRO ARG
+    COBJ "detcom:dheader 532.1 GUINAME2 \"\ARG\" \"TCS guider #2 identification\""
+    ENDM
+DETCOM_GUIEQUI2 MACRO ARG
+    COBJ "detcom:dheader 532.21 GUIEQUI2 \"\ARG\" \"TCS guider #2 equinox\""
+    ENDM
+DETCOM_GUIRADE2 MACRO ARG
+    COBJ "detcom:dheader 532.22 GUIRADE2 \"\ARG\" \"TCS guider #2 system for equinox\""
+    ENDM
+DETCOM_GUIRA2 MACRO ARG
+    COBJ "detcom:dheader 532.23 GUIRA2 \"\ARG\" \"TCS guider #2 right ascension\""
+    ENDM
+DETCOM_GUIDEC2 MACRO ARG
+    COBJ "detcom:dheader 532.24 GUIDEC2 \"\ARG\" \"TCS guider #2 declination\""
+    ENDM
+DETCOM_GUIOBJN2 MACRO ARG
+    COBJ "detcom:dheader 532.3 GUIOBJN2 \"\ARG\" \"TCS guider #2 object name\""
+    ENDM
+DETCOM_GUIFLUX2 MACRO ARG
+    COBJ "detcom:dheader 532.6 GUIFLUX2 \"\ARG\" \"TCS guider #2 flux\""
+    ENDM
+DETCOM_GUINAME3 MACRO ARG
+    COBJ "detcom:dheader 533.1 GUINAME3 \"\ARG\" \"TCS guider #3 identification\""
+    ENDM
+DETCOM_GUIEQUI3 MACRO ARG
+    COBJ "detcom:dheader 533.21 GUIEQUI3 \"\ARG\" \"TCS guider #3 equinox\""
+    ENDM
+DETCOM_GUIRADE3 MACRO ARG
+    COBJ "detcom:dheader 533.22 GUIRADE3 \"\ARG\" \"TCS guider #3 system for equinox\""
+    ENDM
+DETCOM_GUIRA3 MACRO ARG
+    COBJ "detcom:dheader 533.23 GUIRA3 \"\ARG\" \"TCS guider #3 right ascension\""
+    ENDM
+DETCOM_GUIDEC3 MACRO ARG
+    COBJ "detcom:dheader 533.24 GUIDEC3 \"\ARG\" \"TCS guider #3 declination\""
+    ENDM
+DETCOM_GUIOBJN3 MACRO ARG
+    COBJ "detcom:dheader 533.3 GUIOBJN3 \"\ARG\" \"TCS guider #3 object name\""
+    ENDM
+DETCOM_GUIFLUX3 MACRO ARG
+    COBJ "detcom:dheader 533.6 GUIFLUX3 \"\ARG\" \"TCS guider #3 flux\""
+    ENDM
+DETCOM_GUINAME4 MACRO ARG
+    COBJ "detcom:dheader 534.1 GUINAME4 \"\ARG\" \"TCS guider #4 identification\""
+    ENDM
+DETCOM_GUIEQUI4 MACRO ARG
+    COBJ "detcom:dheader 534.21 GUIEQUI4 \"\ARG\" \"TCS guider #4 equinox\""
+    ENDM
+DETCOM_GUIRADE4 MACRO ARG
+    COBJ "detcom:dheader 534.22 GUIRADE4 \"\ARG\" \"TCS guider #4 system for equinox\""
+    ENDM
+DETCOM_GUIRA4 MACRO ARG
+    COBJ "detcom:dheader 534.23 GUIRA4 \"\ARG\" \"TCS guider #4 right ascension\""
+    ENDM
+DETCOM_GUIDEC4 MACRO ARG
+    COBJ "detcom:dheader 534.24 GUIDEC4 \"\ARG\" \"TCS guider #4 declination\""
+    ENDM
+DETCOM_GUIOBJN4 MACRO ARG
+    COBJ "detcom:dheader 534.3 GUIOBJN4 \"\ARG\" \"TCS guider #4 object name\""
+    ENDM
+DETCOM_GUIFLUX4 MACRO ARG
+    COBJ "detcom:dheader 534.6 GUIFLUX4 \"\ARG\" \"TCS guider #4 flux\""
+    ENDM
+DETCOM_FOCUSID MACRO ARG
+    COBJ "detcom:dheader 550.0 FOCUSID \"\ARG\" \"Telescope focus in use\""
+    ENDM
+DETCOM_TELCONF MACRO ARG
+    COBJ "detcom:dheader 550.1 TELCONF \"\ARG\" \"Telescope focus in use\""
+    ENDM
+DETCOM_FOCUSPOS MACRO ARG
+    COBJ "detcom:dheader 551.0 FOCUSPOS \"\ARG\" \"Telescope focus encoder readout\""
+    ENDM
+DETCOM_TELFOCUS MACRO ARG
+    COBJ "detcom:dheader 551.1 TELFOCUS \"\ARG\" \"Telescope focus encoder readout\""
+    ENDM
+DETCOM_ISUSTATE MACRO ARG
+    COBJ "detcom:dheader 560.4 ISUSTATE \"\ARG\" \"ISU control state (Off/Only/TCS/Full/Frozen)\""
+    ENDM
+DETCOM_FSASTATE MACRO ARG
+    COBJ "detcom:dheader 570.4 FSASTATE \"\ARG\" \"FSA control state (Off/On/Paused)\""
+    ENDM
+DETCOM_FSANMOVE MACRO ARG
+    COBJ "detcom:dheader 570.5 FSANMOVE \"\ARG\" \"FSA number of actual moves in last 10 minutes\""
+    ENDM
+DETCOM_TCSGPSBC MACRO ARG
+    COBJ "detcom:dheader 580.11 TCSGPSBC \"\ARG\" \"TCS GPS read out in BCD\""
+    ENDM
+DETCOM_TCSRBUSS MACRO ARG
+    COBJ "detcom:dheader 580.13 TCSRBUSS \"\ARG\" \"TCS RBUSS clock time\""
+    ENDM
+DETCOM_TCSEPICS MACRO ARG
+    COBJ "detcom:dheader 580.14 TCSEPICS \"\ARG\" \"TCS EPICS clock time\""
+    ENDM
+DETCOM_TCSAMODE MACRO ARG
+    COBJ "detcom:dheader 580.15 TCSAMODE \"\ARG\" \"TCS acquisition mode - T/O/G/g for t/o/g coords\""
+    ENDM
+DETCOM_TCSENHA MACRO ARG
+    COBJ "detcom:dheader 580.51 TCSENHA \"\ARG\" \"TCS hour angle encoder bits reading\""
+    ENDM
+DETCOM_TCSENDEC MACRO ARG
+    COBJ "detcom:dheader 580.52 TCSENDEC \"\ARG\" \"TCS declination encoder bits reading\""
+    ENDM
+DETCOM_AOBLOOP MACRO ARG
+    COBJ "detcom:dheader 711.0 AOBLOOP \"\ARG\" \"AOB closed loop Open/Closed\""
+    ENDM
+DETCOM_LOOPNES MACRO ARG
+    COBJ "detcom:dheader 713.0 LOOPNES \"\ARG\" \"Loop type \"Nested\"/\"Tip/Tilt\"/\"Single\"\""
+    ENDM
+DETCOM_LOOPOPT MACRO ARG
+    COBJ "detcom:dheader 715.0 LOOPOPT \"\ARG\" \"Optimized loop control True/False\""
+    ENDM
+DETCOM_WFSGOPT MACRO ARG
+    COBJ "detcom:dheader 716.0 WFSGOPT \"\ARG\" \"WFS optical gain\""
+    ENDM
+DETCOM_WFSCOUNT MACRO ARG
+    COBJ "detcom:dheader 722.0 WFSCOUNT \"\ARG\" \"Total flux count on WFS\""
+    ENDM
+DETCOM_ADCPOS MACRO ARG
+    COBJ "detcom:dheader 751.0 ADCPOS \"\ARG\" \"AOB ADC position In/Out\""
+    ENDM
+DETCOM_BEAMSPID MACRO ARG
+    COBJ "detcom:dheader 754.0 BEAMSPID \"\ARG\" \"Beam splitter ID\""
+    ENDM
+DETCOM_BEAMSP MACRO ARG
+    COBJ "detcom:dheader 755.0 BEAMSP \"\ARG\" \"Beam splitter description\""
+    ENDM
+DETCOM_MIRSLIDE MACRO ARG
+    COBJ "detcom:dheader 756.0 MIRSLIDE \"\ARG\" \"AOB mirror slide \"F/20\"/\"F/8\"\""
+    ENDM
+DETCOM_WFSNDID MACRO ARG
+    COBJ "detcom:dheader 757.0 WFSNDID \"\ARG\" \"WFS ND filter position\""
+    ENDM
+DETCOM_FFLAMPON MACRO ARG
+    COBJ "detcom:dheader 910.0 FFLAMPON \"\ARG\" \"flatfield lamp status ON/OFF\""
+    ENDM
+DETCOM_FFLAMP MACRO ARG
+    COBJ "detcom:dheader 911.0 FFLAMP \"\ARG\" \"flatfield lamp intensity\""
+    ENDM
+DETCOM_CLAMP0ON MACRO ARG
+    COBJ "detcom:dheader 920.0 CLAMP0ON \"\ARG\" \"comparison lamp 0 status ON/OFF\""
+    ENDM
+DETCOM_CLAMP0 MACRO ARG
+    COBJ "detcom:dheader 920.1 CLAMP0 \"\ARG\" \"comparison lamp 0 description\""
+    ENDM
+DETCOM_CLAMP1ON MACRO ARG
+    COBJ "detcom:dheader 921.0 CLAMP1ON \"\ARG\" \"comparison lamp 1 status ON/OFF\""
+    ENDM
+DETCOM_CLAMP1 MACRO ARG
+    COBJ "detcom:dheader 921.1 CLAMP1 \"\ARG\" \"comparison lamp 1 description\""
+    ENDM
+DETCOM_CLAMP2ON MACRO ARG
+    COBJ "detcom:dheader 922.0 CLAMP2ON \"\ARG\" \"comparison lamp 2 status ON/OFF\""
+    ENDM
+DETCOM_CLAMP2 MACRO ARG
+    COBJ "detcom:dheader 922.1 CLAMP2 \"\ARG\" \"comparison lamp 2 description\""
+    ENDM
+DETCOM_CLAMP3ON MACRO ARG
+    COBJ "detcom:dheader 923.0 CLAMP3ON \"\ARG\" \"comparison lamp 3 status ON/OFF\""
+    ENDM
+DETCOM_CLAMP3 MACRO ARG
+    COBJ "detcom:dheader 923.1 CLAMP3 \"\ARG\" \"comparison lamp 3 description\""
+    ENDM
+DETCOM_CALIBL0 MACRO ARG
+    COBJ "detcom:dheader 930.0 CALIBL0 \"\ARG\" \"lamp 0 ON/OFF\""
+    ENDM
+DETCOM_CALIBL1 MACRO ARG
+    COBJ "detcom:dheader 931.0 CALIBL1 \"\ARG\" \"lamp 1 ON/OFF\""
+    ENDM
+DETCOM_CALIBL2 MACRO ARG
+    COBJ "detcom:dheader 932.0 CALIBL2 \"\ARG\" \"lamp 2 ON/OFF\""
+    ENDM
+DETCOM_CALIBL3 MACRO ARG
+    COBJ "detcom:dheader 933.0 CALIBL3 \"\ARG\" \"lamp 3 ON/OFF\""
+    ENDM
+DETCOM_CALIBL4 MACRO ARG
+    COBJ "detcom:dheader 934.0 CALIBL4 \"\ARG\" \"lamp 4 ON/OFF\""
+    ENDM
+DETCOM_CALIBL5 MACRO ARG
+    COBJ "detcom:dheader 935.0 CALIBL5 \"\ARG\" \"lamp 5 ON/OFF\""
+    ENDM
+DETCOM_CALIBL6 MACRO ARG
+    COBJ "detcom:dheader 936.0 CALIBL6 \"\ARG\" \"lamp 6 ON/OFF\""
+    ENDM
+DETCOM_CALIBL7 MACRO ARG
+    COBJ "detcom:dheader 937.0 CALIBL7 \"\ARG\" \"lamp 7 ON/OFF\""
+    ENDM
+DETCOM_CALIBL8 MACRO ARG
+    COBJ "detcom:dheader 938.0 CALIBL8 \"\ARG\" \"lamp 8 ON/OFF\""
+    ENDM
+DETCOM_CALIBL9 MACRO ARG
+    COBJ "detcom:dheader 939.0 CALIBL9 \"\ARG\" \"lamp 9 ON/OFF\""
+    ENDM
+DETCOM_FILTERID MACRO ARG
+    COBJ "detcom:dheader 1010.1 FILTERID \"\ARG\" \"wheel position\""
+    ENDM
+DETCOM_FILTER MACRO ARG
+    COBJ "detcom:dheader 1010.2 FILTER \"\ARG\" \"description of filter\""
+    ENDM
+DETCOM_FILTERBW MACRO ARG
+    COBJ "detcom:dheader 1010.3 FILTERBW \"\ARG\" \"filter bandwidth\""
+    ENDM
+DETCOM_FILTERWL MACRO ARG
+    COBJ "detcom:dheader 1010.4 FILTERWL \"\ARG\" \"filter wavelength\""
+    ENDM
+DETCOM_FILTERLB MACRO ARG
+    COBJ "detcom:dheader 1010.5 FILTERLB \"\ARG\" \"lower bound of filter (units/%?)\""
+    ENDM
+DETCOM_FILTERUB MACRO ARG
+    COBJ "detcom:dheader 1010.6 FILTERUB \"\ARG\" \"upper bound of filter (units/%?)\""
+    ENDM
+DETCOM_FILTSLID MACRO ARG
+    COBJ "detcom:dheader 1010.7 FILTSLID \"\ARG\" \"filter drawer number\""
+    ENDM
+DETCOM_WHEELAID MACRO ARG
+    COBJ "detcom:dheader 1020.1 WHEELAID \"\ARG\" \"'wheel' A position\""
+    ENDM
+DETCOM_WHEELADE MACRO ARG
+    COBJ "detcom:dheader 1020.2 WHEELADE \"\ARG\" \"description of filter\""
+    ENDM
+DETCOM_WHEELALB MACRO ARG
+    COBJ "detcom:dheader 1020.5 WHEELALB \"\ARG\" \"lower bound of filter A (units/%?)\""
+    ENDM
+DETCOM_WHEELAUB MACRO ARG
+    COBJ "detcom:dheader 1020.6 WHEELAUB \"\ARG\" \"upper bound of filter A (units/%?)\""
+    ENDM
+DETCOM_WHEELBID MACRO ARG
+    COBJ "detcom:dheader 1030.1 WHEELBID \"\ARG\" \"'wheel' B position\""
+    ENDM
+DETCOM_WHEELBDE MACRO ARG
+    COBJ "detcom:dheader 1030.2 WHEELBDE \"\ARG\" \"description of filter\""
+    ENDM
+DETCOM_WHEELBLB MACRO ARG
+    COBJ "detcom:dheader 1030.5 WHEELBLB \"\ARG\" \"lower bound of filter B (units/%?)\""
+    ENDM
+DETCOM_WHEELBUB MACRO ARG
+    COBJ "detcom:dheader 1030.6 WHEELBUB \"\ARG\" \"upper bound of filter B (units/%?)\""
+    ENDM
+DETCOM_INSTFOC MACRO ARG
+    COBJ "detcom:dheader 1101.0 INSTFOC \"\ARG\" \"internal instrument focus value\""
+    ENDM
+DETCOM_GRISMID MACRO ARG
+    COBJ "detcom:dheader 4101.0 GRISMID \"\ARG\" \"grism position\""
+    ENDM
+DETCOM_GRISM MACRO ARG
+    COBJ "detcom:dheader 4102.0 GRISM \"\ARG\" \"grism description\""
+    ENDM
+DETCOM_GRISSLID MACRO ARG
+    COBJ "detcom:dheader 4103.0 GRISSLID \"\ARG\" \"grism drawer number\""
+    ENDM
+DETCOM_MASKID MACRO ARG
+    COBJ "detcom:dheader 4111.0 MASKID \"\ARG\" \"mask position\""
+    ENDM
+DETCOM_MASK MACRO ARG
+    COBJ "detcom:dheader 4112.0 MASK \"\ARG\" \"mask description\""
+    ENDM
+DETCOM_MASKSLID MACRO ARG
+    COBJ "detcom:dheader 4113.0 MASKSLID \"\ARG\" \"mask slider number\""
+    ENDM
+DETCOM_DISPEL MACRO ARG
+    COBJ "detcom:dheader 4201.0 DISPEL \"\ARG\" \"grating description\""
+    ENDM
+DETCOM_DISPAXIS MACRO ARG
+    COBJ "detcom:dheader 4202.0 DISPAXIS \"\ARG\" \"dispersion axis (1 or 2)\""
+    ENDM
+DETCOM_DISPANG MACRO ARG
+    COBJ "detcom:dheader 4203.0 DISPANG \"\ARG\" \"grating angle in degrees\""
+    ENDM
+DETCOM_WAVELENG MACRO ARG
+    COBJ "detcom:dheader 4204.0 WAVELENG \"\ARG\" \"central wavelength in angstroms\""
+    ENDM
+DETCOM_ORDER MACRO ARG
+    COBJ "detcom:dheader 4205.0 ORDER \"\ARG\" \"spectral order\""
+    ENDM
+DETCOM_GRSETUP MACRO ARG
+    COBJ "detcom:dheader 4206.0 GRSETUP \"\ARG\" \"grating setup constant in degrees\""
+    ENDM
+DETCOM_OPENANG2 MACRO ARG
+    COBJ "detcom:dheader 4207.0 OPENANG2 \"\ARG\" \"setup const in degrees (opening angle / 2)\""
+    ENDM
+DETCOM_GRISMANG MACRO ARG
+    COBJ "detcom:dheader 4208.0 GRISMANG \"\ARG\" \"rel.angle between prisms in degrees\""
+    ENDM
+DETCOM_SLICER MACRO ARG
+    COBJ "detcom:dheader 4210.0 SLICER \"\ARG\" \"image slicer in place\""
+    ENDM
+DETCOM_HART1A MACRO ARG
+    COBJ "detcom:dheader 4221.0 HART1A \"\ARG\" \"hartman mask 1a position OPEN/CLOSED\""
+    ENDM
+DETCOM_HART1B MACRO ARG
+    COBJ "detcom:dheader 4221.1 HART1B \"\ARG\" \"hartman mask 1b position OPEN/CLOSED\""
+    ENDM
+DETCOM_HART2 MACRO ARG
+    COBJ "detcom:dheader 4222.0 HART2 \"\ARG\" \"hartman mask 2 position OPEN/CLOSED\""
+    ENDM
+DETCOM_HART3 MACRO ARG
+    COBJ "detcom:dheader 4223.0 HART3 \"\ARG\" \"hartman mask 3 position OPEN/CLOSED\""
+    ENDM
+DETCOM_HART4 MACRO ARG
+    COBJ "detcom:dheader 4224.0 HART4 \"\ARG\" \"hartman mask 4 position OPEN/CLOSED\""
+    ENDM
+DETCOM_COUDETRN MACRO ARG
+    COBJ "detcom:dheader 4230.0 COUDETRN \"\ARG\" \"coude train color UV/RED/CAFE\""
+    ENDM
+DETCOM_EMFILTER MACRO ARG
+    COBJ "detcom:dheader 4240.0 EMFILTER \"\ARG\" \"exposure meter filter description\""
+    ENDM
+DETCOM_EMCNTS MACRO ARG
+    COBJ "detcom:dheader 4241.0 EMCNTS \"\ARG\" \"exposure meter counts at end\""
+    ENDM
+DETCOM_MIDEXPTM MACRO ARG
+    COBJ "detcom:dheader 4242.0 MIDEXPTM \"\ARG\" \"mid-exposure time (seconds)\""
+    ENDM
+DETCOM_CAFE MACRO ARG
+    COBJ "detcom:dheader 4290.0 CAFE \"\ARG\" \"CAFE is being used Null/INUSE\""
+    ENDM
+DETCOM_AUXINST MACRO ARG
+    COBJ "detcom:dheader 4301.0 AUXINST \"\ARG\" \"fabry perot etalon description\""
+    ENDM
+DETCOM_NUMCHAN MACRO ARG
+    COBJ "detcom:dheader 4303.0 NUMCHAN \"\ARG\" \"number of channels per scan\""
+    ENDM
+DETCOM_CURCHAN MACRO ARG
+    COBJ "detcom:dheader 4304.0 CURCHAN \"\ARG\" \"current channel\""
+    ENDM
+DETCOM_BINVAL MACRO ARG
+    COBJ "detcom:dheader 4305.0 BINVAL \"\ARG\" \"binary control value\""
+    ENDM
+DETCOM_GRFPPOS MACRO ARG
+    COBJ "detcom:dheader 4320.0 GRFPPOS \"\ARG\" \"GriF position of FP carriage - In/Out\""
+    ENDM
+DETCOM_GRWAVORD MACRO ARG
+    COBJ "detcom:dheader 4321.1 GRWAVORD \"\ARG\" \"GriF wavelength of order used to scan\""
+    ENDM
+DETCOM_GRWAVCAL MACRO ARG
+    COBJ "detcom:dheader 4321.2 GRWAVCAL \"\ARG\" \"GriF wavelength of calibration spectral line\""
+    ENDM
+DETCOM_GRBCVCAL MACRO ARG
+    COBJ "detcom:dheader 4321.3 GRBCVCAL \"\ARG\" \"GriF BCV at calibration line\""
+    ENDM
+DETCOM_GRSCANTY MACRO ARG
+    COBJ "detcom:dheader 4322.0 GRSCANTY \"\ARG\" \"GriF type of scan - bcv, wave, velocity\""
+    ENDM
+DETCOM_GRWAVBEG MACRO ARG
+    COBJ "detcom:dheader 4323.1 GRWAVBEG \"\ARG\" \"GriF wavelength at beginning of scan\""
+    ENDM
+DETCOM_GRWAVSTP MACRO ARG
+    COBJ "detcom:dheader 4323.2 GRWAVSTP \"\ARG\" \"GriF wavelength step\""
+    ENDM
+DETCOM_GRWAVCUR MACRO ARG
+    COBJ "detcom:dheader 4323.3 GRWAVCUR \"\ARG\" \"GriF current observed wavelength\""
+    ENDM
+DETCOM_GRBCVBEG MACRO ARG
+    COBJ "detcom:dheader 4324.1 GRBCVBEG \"\ARG\" \"GriF Binary Contol Value at beginning of scan\""
+    ENDM
+DETCOM_GRBCVSTP MACRO ARG
+    COBJ "detcom:dheader 4324.2 GRBCVSTP \"\ARG\" \"GriF BCV step\""
+    ENDM
+DETCOM_GRBCVCUR MACRO ARG
+    COBJ "detcom:dheader 4324.3 GRBCVCUR \"\ARG\" \"GriF applied BCV\""
+    ENDM
+DETCOM_GRNBCHAN MACRO ARG
+    COBJ "detcom:dheader 4325.1 GRNBCHAN \"\ARG\" \"GriF number of channels in scan\""
+    ENDM
+DETCOM_GRCURRCH MACRO ARG
+    COBJ "detcom:dheader 4325.2 GRCURRCH \"\ARG\" \"GriF current channel\""
+    ENDM
+DETCOM_GRBCV_X MACRO ARG
+    COBJ "detcom:dheader 4326.1 GRBCV_X \"\ARG\" \"GriF X Binary Control Value FP\""
+    ENDM
+DETCOM_GRBCV_Y MACRO ARG
+    COBJ "detcom:dheader 4326.2 GRBCV_Y \"\ARG\" \"GriF Y Binary Control Value FP\""
+    ENDM
+DETCOM_CRUNID MACRO ARG
+    COBJ "detcom:dheader 15101.0 CRUNID \"\ARG\" \"Elixir camera run ID\""
+    ENDM
Index: /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfh/fh_registry.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfh/fh_registry.h	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfh/fh_registry.h	(revision 23594)
@@ -0,0 +1,1025 @@
+/*                                             -*- c-file-style: "Ellemtel" -*-
+
+`fh_registry.h' - A registry of FITS keywords in use at CFHT.
+
+This file is part of version 1 of the FITS Handling Library.
+Read the `License' file for terms of use and distribution.
+Copyright 2001, Canada-France-Hawaii Telescope, daprog@cfht.hawaii.edu.
+
+___This header automatically generated from `Index'. Do not edit it here!___ */
+
+/*
+ * $Id: fh_registry.h,v 1.19 2003/11/25 21:06:28 thomas Exp $
+ *
+ * Documentation: See http://software.cfht.hawaii.edu/fh_registry/
+ */
+
+#ifndef _INCLUDED_fh_registry
+#define _INCLUDED_fh_registry 1
+
+#if defined(__GNUC__) && defined(__STDC__)
+static const char fh_registry_rcs_id[] __attribute__ ((__unused__)) = "@(#) $Id: fh_registry.h,v 1.19 2003/11/25 21:06:28 thomas Exp $" ;
+#else
+static const char fh_registry_rcs_id[] = "@(#) $Id: fh_registry.h,v 1.19 2003/11/25 21:06:28 thomas Exp $";
+#endif
+
+/*
+ * These are environment variables which the CFHT "ccd" process (both
+ * script and older C program) make available to instrument handler programs.
+ */
+#define ENV_FFTEMPLATE	"FFTEMPLATE"
+#define ENV_OBSTYPE	"OBSTYPE"
+#define ENV_INTTIME	"INTTIME"
+#define ENV_EXPNUM	"EXPNUM"
+#define ENV_SEQNUM	"SEQNUM"
+
+/*
+ * This macro is used inside the other macros (ID_FLT, ID_INT, etc.)
+ * so that the keyword names can be referred to as fh_kw_FOO
+ * This can be used as an identifier to fh_get() or any other function
+ * instead of using the string constant "FOO".
+ */
+
+#define ID_FLT(idx, name, keyword, prec, comment) \
+static inline void \
+fh_set_##name (HeaderUnit hu, double value) \
+{ fh_set_flt(hu, idx, keyword, value, prec, comment); } \
+static inline void \
+fh_setval_##name (HeaderUnit hu, const char* value) \
+{ fh_set_val(hu, idx, keyword, value, comment); } \
+static inline void \
+fh_setcmt_##name (HeaderUnit hu, double value, const char* new_comment) \
+{ fh_set_flt(hu, idx, keyword, value, prec, new_comment); } \
+static inline fh_result \
+fh_get_##name (HeaderUnit hu, double* value) \
+{ return fh_get_flt(hu, keyword, value); } \
+static inline const char* \
+fh_kw_##name(void) { return keyword; } \
+static inline const char* \
+fh_cmt_##name(void) { return comment; }
+
+#define ID_PFL(idx, name, keyword, prec, comment) \
+static inline void \
+fh_set_##name (HeaderUnit hu, double value) \
+{ fh_set_pfl(hu, idx, keyword, value, prec, comment); } \
+static inline void \
+fh_setval_##name (HeaderUnit hu, const char* value) \
+{ fh_set_val(hu, idx, keyword, value, comment); } \
+static inline void \
+fh_setcmt_##name (HeaderUnit hu, double value, const char* new_comment) \
+{ fh_set_pfl(hu, idx, keyword, value, prec, new_comment); } \
+static inline fh_result \
+fh_get_##name (HeaderUnit hu, double* value) \
+{ return fh_get_flt(hu, keyword, value); } \
+static inline const char* \
+fh_kw_##name(void) { return keyword; } \
+static inline const char* \
+fh_cmt_##name(void) { return comment; }
+
+#define ID_INT(idx, name, keyword, comment) \
+static inline void \
+fh_set_##name (HeaderUnit hu, int value) \
+{ fh_set_int(hu, idx, keyword, value, comment); } \
+static inline void \
+fh_setval_##name (HeaderUnit hu, const char* value) \
+{ fh_set_val(hu, idx, keyword, value, comment); } \
+static inline void \
+fh_setcmt_##name (HeaderUnit hu, int value, const char* new_comment) \
+{ fh_set_int(hu, idx, keyword, value, new_comment); } \
+static inline fh_result \
+fh_get_##name (HeaderUnit hu, int* value) \
+{ return fh_get_int(hu, keyword, value); } \
+static inline const char* \
+fh_kw_##name(void) { return keyword; } \
+static inline const char* \
+fh_cmt_##name(void) { return comment; }
+
+#define ID_BLN(idx, name, keyword, comment) \
+static inline void \
+fh_set_##name (HeaderUnit hu, fh_bool value) \
+{ fh_set_bool(hu, idx, keyword, value, comment); } \
+static inline void \
+fh_setval_##name (HeaderUnit hu, const char* value) \
+{ fh_set_val(hu, idx, keyword, value, comment); } \
+static inline void \
+fh_setcmt_##name (HeaderUnit hu, fh_bool value, const char* new_comment) \
+{ fh_set_bool(hu, idx, keyword, value, new_comment); } \
+static inline fh_result \
+fh_get_##name (HeaderUnit hu, fh_bool* value) \
+{ return fh_get_bool(hu, keyword, value); } \
+static inline const char* \
+fh_kw_##name(void) { return keyword; } \
+static inline const char* \
+fh_cmt_##name(void) { return comment; }
+
+#define ID_STR(idx, name, keyword, comment) \
+static inline void \
+fh_set_##name (HeaderUnit hu, const char* value) \
+{ fh_set_str(hu, idx, keyword, value, comment); } \
+static inline void \
+fh_setval_##name (HeaderUnit hu, const char* value) \
+{ fh_set_val(hu, idx, keyword, value, comment); } \
+static inline void \
+fh_setcmt_##name (HeaderUnit hu, const char* value, const char* new_comment) \
+{ fh_set_str(hu, idx, keyword, value, new_comment); } \
+static inline fh_result \
+fh_get_##name (HeaderUnit hu, char* value, int maxlen) \
+{ return fh_get_str(hu, keyword, value, maxlen); } \
+static inline const char* \
+fh_kw_##name(void) { return keyword; } \
+static inline const char* \
+fh_cmt_##name(void) { return comment; }
+
+#define ID_VAL(idx, name, keyword, comment) \
+static inline void \
+fh_set_##name (HeaderUnit hu, const char* value) \
+{ fh_set_val(hu, idx, keyword, value, comment); } \
+static inline void \
+fh_setcmt_##name (HeaderUnit hu, const char* value, const char* new_comment) \
+{ fh_set_val(hu, idx, keyword, value, new_comment); } \
+static inline const char* \
+fh_kw_##name(void) { return keyword; } \
+static inline const char* \
+fh_cmt_##name(void) { return comment; }
+
+#define ID_CMT(idx, name, comment) \
+static inline void \
+fh_set_##name (HeaderUnit hu) \
+{ fh_set_com(hu, idx, "COMMENT", comment); }
+
+/*
+ * Now the auto-stringified versions of ID_*() ...
+ */
+#define MK_FLT(idx, name, dig, comment) ID_FLT(idx, name, #name, dig, comment)
+#define MK_PFL(idx, name, dec, comment) ID_PFL(idx, name, #name, dec, comment)
+#define MK_INT(idx, name, comment) ID_INT(idx, name, #name, comment)
+#define MK_STR(idx, name, comment) ID_STR(idx, name, #name, comment)
+#define MK_VAL(idx, name, comment) ID_VAL(idx, name, #name, comment)
+#define MK_BLN(idx, name, comment) ID_BLN(idx, name, #name, comment)
+#define MK_CMT(idx, name, comment) ID_CMT(idx, name, comment)
+
+/*
+ * Description of columns:
+ *
+ *      TYPE - MK_BLN, MK_STR, MK_INT, MK_FLT, MK_PFL selects a type for the
+ *             keyword (True/False, 'String  ', integer, or floating point.)
+ *             Any keyword can still be set to any type with fh_setval_*(),
+ *             which takes a pre-formatted character string for the card.
+ *
+ *      IDX  - Index sorting number, which eventually determines the final
+ *             order of keywords in the FITS header.  These are floating point
+ *             values so new keywords can always be inserted in between
+ *             without shifting all the values.
+ *
+ *   KEYWORD - The name of the keyword.  Macros will be generated of the form
+ *             fh_get_KEYWORD(), fh_set_KEYWORD(), and fh_setval_KEYWORD().
+ *
+ *      PRC  - For MK_FLT(), this specifies the (scientific) precision,
+ *             or number of significant digits in the value.  If necessary,
+ *             scientific notation will be used to format the value (see
+ *             rules for printf "%.*G").
+ *             For MK_PFL(), this specifies mathematical precision, or number
+ *             of decimal places in the formatted value (see the rules for
+ *             printf "%.*f").
+ *
+ *             Use MK_PFL() (make "precise float") if that value is always
+ *             precise to an exact number digits after the decimal point.
+ *             Use MK_FLT() if the value might require scientific notation.
+ *             All results are FITS-standard legal.
+ *
+ *   COMMENT - String to be included after the / in the FITS card, if
+ *             there is room within the 80 columns (it may get truncated.)
+ *             Note that in the text below, 80 columns are reached when
+ *             there is still one space between the " and the ) at the end
+ *             of the line.
+ *
+ * FITS Structure : 0.000 to 9.999
+ *
+ * The following keywords give basic information about the structure and
+ * dimensions of the FITS file, and must appear in the specific order
+ * below, usually without any other intervening keywords.
+ *
+ *TYPE --IDX--  KEYWORD- PRC -------------------COMMENT----------------------*/
+MK_BLN( 0.0,	SIMPLE  ,   "Standard FITS"                                   )
+MK_STR( 0.0,	XTENSION,   ""                                                )
+MK_INT( 1.0,	BITPIX  ,   "Bits per pixel"                                  )
+MK_INT( 2.0,	NAXIS   ,   "Number of axes"                                  )
+MK_INT( 2.1,	NAXIS1  ,   "Number of pixel columns"                         )
+MK_INT( 2.2,	NAXIS2  ,   "Number of pixel rows"                            )
+MK_INT( 2.3,	NAXIS3  ,   "Number of stacked frames (cube)"                 )
+MK_BLN( 3.0,	EXTEND  ,   "File contains extensions"                        )
+MK_INT( 3.1,	NEXTEND ,   "Number of extensions"                            )
+MK_BLN( 4.0,	GROUPS  ,   "File contains random groups records"             )
+MK_INT( 5.0,	PCOUNT  ,   "Random parameters before each array in a group"  )
+MK_INT( 6.0,	GCOUNT  ,   "Number of random groups"                         )
+MK_INT( 7.0000,	TFIELDS ,   "Number of fields in a row"                       )
+MK_STR( 7.0011,	TFORM1  ,   "Table format for field 1"                        )
+MK_INT( 7.0012,	TBCOL1  ,   "Start Column for field 1"                        )
+MK_STR( 7.0021,	TFORM2  ,   "Table format for field 2"                        )
+MK_INT( 7.0022,	TBCOL2  ,   "Start Column for field 2"                        )
+MK_STR( 7.0031,	TFORM3  ,   "Table format for field 3"                        )
+MK_INT( 7.0032,	TBCOL3  ,   "Start Column for field 3"                        )
+MK_STR( 7.0041,	TFORM4  ,   "Table format for field 4"                        )
+MK_INT( 7.0042,	TBCOL4  ,   "Start Column for field 4"                        )
+MK_STR( 7.0051,	TFORM5  ,   "Table format for field 5"                        )
+MK_INT( 7.0052,	TBCOL5  ,   "Start Column for field 5"                        )
+MK_PFL( 10.0,   BZERO   ,1, "Zero factor"                                     )
+MK_PFL( 11.0,	BSCALE  ,1, "Scale factor"                                    )
+
+/*
+ * Summary : 50.000 to 59.999
+ *
+ *TYPE --IDX--  KEYWORD- PRC -------------------COMMENT----------------------*/
+MK_CMT(   50.1, CMTSUM1 ,   ""                                                )
+MK_CMT(   50.2, CMTSUM2 ,   "Observation Summary"                             )
+MK_CMT(   50.3, CMTSUM3 ,   "-------------------"                             )
+MK_CMT(   50.4, CMTSUM4 ,   ""                                                )
+MK_CMT(   50.5, CMTSUM5 ,   ""                                                )
+MK_STR(   51.0, CMMTOBS ,   ""                                                )
+MK_STR(   52.0, CMMTSEQ ,   ""                                                )
+MK_STR(   53.0, OBJECT  ,   ""                                                )
+MK_STR(   54.0, OBSERVER,   ""                                                )
+MK_STR(   55.0, PI_NAME ,   ""                                                )
+MK_STR(   56.0, RUNID   ,   ""                                                )
+
+/*
+ * General : 70.000 to 79.999
+ *
+ *TYPE --IDX--  KEYWORD- PRC -------------------COMMENT----------------------*/
+MK_CMT(  70.10, CMTGEN1 ,   ""                                                )
+MK_CMT(  70.20, CMTGEN2 ,   "General"                                         )
+MK_CMT(  70.30, CMTGEN3 ,   "-------"                                         )
+MK_CMT(  70.40, CMTGEN4 ,   ""                                                )
+MK_STR(  71.00, FILENAME,   "Base filename at acquisition"                    )
+MK_STR(  71.01, PATHNAME,   "Original directory name at acquisition"          )
+MK_STR(  71.10, EXTNAME ,   "Extension name"                                  )
+MK_INT(  71.20, EXTVER  ,   "Extension version"                               )
+MK_STR(  74.00, DATE    ,   "UTC Date of file creation"                       )
+MK_STR(  74.10, HSTTIME ,   "Local time in Hawaii"                            )
+MK_STR(  77.00, IMAGESWV,   "Image creation software version"                 )
+
+/*
+ * Summary : 90.0 Key stuff from instrument, detector (and other?) sections
+ */
+MK_STR(   91.0,	DETECTOR,   "Science Detector"                                )
+MK_STR(   92.0, INSTRUME,   "Instrument Name"                                 )
+MK_STR(   92.1, INSTMODE,   "Instrument Mode"                                 )
+
+/*
+ * Detector : 100.000 to 199.999
+ *
+ *TYPE --IDX--  KEYWORD- PRC -------------------COMMENT----------------------*/
+MK_CMT(  100.1,	CMTDET1	,   ""                                                )
+MK_CMT(  100.2,	CMTDET2	,   "Detector"                                        )
+MK_CMT(  100.3,	CMTDET3	,   "--------"                                        )
+MK_CMT(  100.4,	CMTDET4	,   ""                                                )
+MK_STR(  102.0, DETSIZE	,   "Total data pixels in full mosaic"                )
+MK_STR(  103.0,	RASTER	,   ""                                                )
+MK_STR(  105.0,	CCDSUM	,   "Binning factors"                                 )
+MK_INT(  105.1,	CCDBIN1	,   "Binning factor along first axis"                 )
+MK_INT(  105.2,	CCDBIN2	,   "Binning factor along second axis"                )
+MK_FLT(  110.0,	PIXSIZE ,3, "Pixel size for both axes (microns)"              )
+MK_FLT(  111.1,	PIXSIZE1,3, "Pixel size for axis 1 (microns)"                 )
+MK_FLT(  111.2,	PIXSIZE2,3, "Pixel size for axis 2 (microns)"                 )
+MK_FLT(  112.1,	PIXSCAL1,4, "Pixel scale for axis 1 (arcsec/pixel)"           )
+MK_FLT(  112.2,	PIXSCAL2,4, "Pixel scale for axis 2 (arcsec/pixel)"           )
+MK_STR(  130.0,	AMPLIST	,   "List of amplifiers for this image"               )
+MK_STR(  131.0,	AMPNAME	,   "Amplifier name"                                  )
+MK_STR(  140.0,	CCDSIZE ,   "Detector imaging area size"                      )
+MK_STR(  140.1,	DETSEC  ,   "Mosaic area of the detector"                     )
+MK_STR(  140.11,DETSECA ,   "Mosaic area of the detector from Amp A"          )
+MK_STR(  140.12,DETSECB ,   "Mosaic area of the detector from Amp B"          )
+MK_STR(  140.13,DETSECC ,   "Mosaic area of the detector from Amp C"          )
+MK_STR(  140.14,DETSECD ,   "Mosaic area of the detector from Amp D"          )
+MK_STR(  140.2,	DATASEC ,   "Imaging area of the detector"                    )
+MK_STR(  140.3,	BIASSEC ,   "Overscan (bias) area of the detector"            )
+MK_STR(  141.11,ASECA   ,   "Section from Amp A (non-contig. bias excluded)"  )
+MK_STR(  141.12,ASECB   ,   "Section from Amp B (non-contig. bias excluded)"  )
+MK_STR(  141.13,ASECC   ,   "Section from Amp C (non-contig. bias excluded)"  )
+MK_STR(  141.14,ASECD   ,   "Section from Amp D (non-contig. bias excluded)"  )
+MK_STR(  141.21,BSECA   ,   "Overscan (bias) area from Amp A"                 )
+MK_STR(  141.22,BSECB   ,   "Overscan (bias) area from Amp B"                 )
+MK_STR(  141.23,BSECC   ,   "Overscan (bias) area from Amp C"                 )
+MK_STR(  141.24,BSECD   ,   "Overscan (bias) area from Amp D"                 )
+MK_STR(  141.31,CSECA   ,   "Section in full CCD for DSECA"                   )
+MK_STR(  141.32,CSECB   ,   "Section in full CCD for DSECB"                   )
+MK_STR(  141.33,CSECC   ,   "Section in full CCD for DSECC"                   )
+MK_STR(  141.34,CSECD   ,   "Section in full CCD for DSECD"                   )
+MK_STR(  141.41,DSECA   ,   "Imaging area from Amp A"                         )
+MK_STR(  141.42,DSECB   ,   "Imaging area from Amp B"                         )
+MK_STR(  141.43,DSECC   ,   "Imaging area from Amp C"                         )
+MK_STR(  141.44,DSECD   ,   "Imaging area from Amp D"                         )
+MK_STR(  141.51,TSECA   ,   "Trim section for Amp A"                          )
+MK_STR(  141.52,TSECB   ,   "Trim section for Amp B"                          )
+MK_STR(  141.53,TSECC   ,   "Trim section for Amp C"                          )
+MK_STR(  141.54,TSECD   ,   "Trim section for Amp D"                          )
+MK_STR(  150.1, CCDNAME,    "Name of the CCD (manufacturer reference)"        )
+MK_STR(  150.2, CCDNICK,    "Nickname of the CCD"                             )
+MK_INT(  152.0, MAXLIN  ,   "Maximum linearity value (ADU)"                   )
+MK_INT(  152.01,MAXLINA ,   "Maximum linearity value for Amp A (ADU)"         )
+MK_INT(  152.02,MAXLINB ,   "Maximum linearity value for Amp B (ADU)"         )
+MK_INT(  152.03,MAXLINC ,   "Maximum linearity value for Amp C (ADU)"         )
+MK_INT(  152.04,MAXLIND ,   "Maximum linearity value for Amp D (ADU)"         )
+MK_INT(  152.1, SATURATE,   "Saturation value (ADU)"                          )
+MK_FLT(  155.0,	GAIN    ,3, "Amplifier gain (electrons/ADU)"                  )
+MK_FLT(  155.01,GAINA   ,3, "Amp A gain (electrons/ADU)"                      )
+MK_FLT(  155.02,GAINB   ,3, "Amp B gain (electrons/ADU)"                      )
+MK_FLT(  155.03,GAINC   ,3, "Amp C gain (electrons/ADU)"                      )
+MK_FLT(  155.04,GAIND   ,3, "Amp D gain (electrons/ADU)"                      )
+MK_FLT(  156.0,	RDNOISE ,3, "Read noise (electrons)"                          )
+MK_FLT(  156.01,RDNOISEA,3, "Amp A read noise (electrons)"                    )
+MK_FLT(  156.02,RDNOISEB,3, "Amp B read noise (electrons)"                    )
+MK_FLT(  156.03,RDNOISEC,3, "Amp C read noise (electrons)"                    )
+MK_FLT(  156.04,RDNOISED,3, "Amp D read noise (electrons)"                    )
+MK_FLT(  157.0, DCURRENT,5, "Dark current (ADU/pixel/second)"                 )
+MK_FLT(  157.1, DARKCUR ,5, "Dark current (e-/pixel/hour)"                    )
+MK_STR(  158.0,	QEPOINTS,   "QE%@wavelength in nm"                            )
+MK_STR(  170.0, CONSWV	,   "Controller software DSPID and SERNO versions"    )
+MK_STR(  180.0,	DETSTAT	,   "Detector status"                                 )
+MK_FLT(  190.0, DETTEM  ,3, "Detector temperature"                            )
+
+/* ****************************************************
+ * *** World Coordinate System : 300.000 to 399.999 ***
+ * ****************************************************
+ *
+ *TYPE --IDX--  KEYWORD- PRC -------------------COMMENT----------------------*/
+
+/*
+ * Telescope : 500.000 to 599.999
+ *
+ * Almost all keywords we define can be generated with the MK_XXX macros.
+ * The only exceptions are those keywords which contain a '-', since the
+ * '-' is not a legal character in a C identifier.  TCS uses four of them
+ * (DATE-OBS, UTC-OBS, MJD-OBS, and LST-OBS.)  The ID_XXX macros are used
+ * and the '-' is translated to a '_' (underscore) for use in C programs.
+ *
+ * NOTE: TELESCOP, TELSTAT, TELCONF, LATITUDE, and LONGITUD are currently
+ * inserted by DetCom and _not_ tcsh.  This should probably be changed.
+ *
+ * Further details on some less obvious keywords:
+ *
+ *   SKYFLUX  - if the guider(s) can generate a reasonable guess for the
+ *              amount of flux currently available for doing flats, that
+ *              number is in this keyword - negative means not available
+ *
+ *   GUIFLUXn - these values are the most recent one minute average of the
+ *              total flux seen in whatever guiding box/area is in use
+ *
+ *   GUIFWH?n - these are the most recent one minute average FWHM values
+ *              for the x/y axes of the guider image(s) recorded at the
+ *              start of the exposure
+ *
+ *   ISUSTDV? - these are the most recent one minute standard deviations
+ *              in the ISU x and y positions in arc seconds recorded
+ *              at the start of the exposure
+ *
+ *   FSAstats - the FSA statistics are for the most recent 10 minutes
+ *              recorded at the start of the exposure
+ *
+ * If there are multiple guiders in use, the unnumbered GUIEQUIN/GUIRADEC
+ * /GUIRA/GUIDEC/GUIRAPM/GUIDECPM values will duplicate the numbered
+ * values for the primary, reference guider.  If there is only one guider
+ * in use, only the unnumbered values should be present.
+ *
+ *TYPE --IDX--  SYMBOL--  KEYWORD-  -----------COMMENT----------------------*/
+ID_STR( 511.1,	DATE_OBS,"DATE-OBS", "Date at start of observation (UTC)"     )
+ID_STR( 512.1,	UTC_OBS ,"UTC-OBS", "Time at start of observation (UTC)"      )
+ID_PFL( 513.1,  MJD_OBS ,"MJD-OBS",7, "Modified Julian Date at start of obs." )
+ID_STR( 514.1,	LST_OBS ,"LST-OBS", "Sidereal time at start of exposure"      )
+
+/*TYPE --IDX--  KEYWORD- PRC -------------------COMMENT----------------------*/
+MK_CMT(  500.1, CMTTCS1 ,   ""                                                )
+MK_CMT(  500.2, CMTTCS2 ,   "Telescope"                                       )
+MK_CMT(  500.3, CMTTCS3 ,   "---------"                                       )
+MK_CMT(  500.4, CMTTCS4 ,   ""                                                )
+MK_STR(  501.1, TELESCOP,   ""                                                )
+MK_STR(  501.2, ORIGIN  ,   "Canada-France-Hawaii Telescope"                  )
+MK_PFL(  502.1, LATITUDE,6, "Latitude (degrees N)"                            )
+MK_PFL(  502.2, LONGITUD,6, "Longitude (degrees E)"                           )
+MK_STR(  503.0, TELSTAT ,   "Telescope Control System status"                 )
+MK_STR(  510.0, TIMESYS ,   "Time System for DATExxxx"                        )
+/* note that the following three have xxx.1 versions above */
+MK_STR(  512.0,	UTIME   ,   "Time at start of observation (UT)"               )
+MK_PFL(  513.0,	MJDATE  ,7 ,"Modified Julian Date at start of observation"    )
+MK_STR(  514.0,	SIDTIME ,   "Sidereal time at start of observation"           )
+MK_VAL(  521.0,	EPOCH   ,   "Equinox of coordinates"                          )
+MK_VAL(  521.1,	EQUINOX ,   "Equinox of coordinates"                          )
+MK_STR(  522.0,	RADECSYS,   "Coordinate system for equinox (FK4/FK5/GAPPT)"   )
+MK_STR(  522.1,	RA      ,   "Object right ascension"                          )
+MK_STR(  522.2,	DEC     ,   "Object declination"                              )
+MK_PFL(  523.1,	RA_DEG  ,6, "Object right ascension in degrees"               )
+MK_PFL(  523.2,	DEC_DEG ,6, "Object declination in degrees"                   )
+
+MK_PFL(525.101,	CRVAL1	,5, "WCS Ref value (RA in decimal degrees)"           )
+MK_PFL(525.102,	CRVAL2	,5, "WCS Ref value (DEC in decimal degrees)"          )
+MK_STR(525.211,	CTYPE1	,   "WCS Coordinate type"                             )
+MK_STR(525.212,	CTYPE2	,   "WCS Coordinate type"                             )
+MK_PFL(525.301,	CRPIX1	,1, "WCS Coordinate reference pixel"                  )
+MK_PFL(525.302,	CRPIX2	,1, "WCS Coordinate reference pixel"                  )
+MK_FLT(525.411,	CD1_1	,6, "WCS Coordinate scale matrix"                     )
+MK_FLT(525.412,	CD1_2	,6, "WCS Coordinate scale matrix"                     )
+MK_FLT(525.421,	CD2_1	,6, "WCS Coordinate scale matrix"                     )
+MK_FLT(525.422,	CD2_2	,6, "WCS Coordinate scale matrix"                     )
+
+MK_INT(  530.0,	NGUIDER	,   "TCS Number of guiders"                           )
+MK_INT( 530.01, NGUISTAR,   "TCS number of guide stars/probes in use"         )
+MK_STR(  530.1, GUINAME ,   "TCS guider name"                                 )
+MK_VAL( 530.21,	GUIEQUIN,   "TCS guider equinox"                              )
+MK_STR( 530.22,	GUIRADEC,   "TCS guider system for equinox"                   )
+MK_STR( 530.23,	GUIRA   ,   "TCS guider right ascension"                      )
+MK_STR( 530.24,	GUIDEC  ,   "TCS guider declination"                          )
+MK_PFL( 530.25, GUIRAPM ,2, "TCS guider right ascension proper motion arcsec" )
+MK_PFL( 530.26, GUIDECPM,2, "TCS guider declination proper motion arcsec"     )
+MK_STR(  530.3, GUIOBJN ,   "TCS guider object name"                          )
+MK_PFL(  530.4, GUIMAGN ,1, "TCS guider object magnitude"                     )
+MK_PFL( 530.51,	XPROBE  ,3 ,"Telescope bonnette guide probe X position"       )
+MK_PFL( 530.52,	YPROBE  ,3 ,"Telescope bonnette guide probe Y position"       )
+MK_PFL( 530.53,	ZPROBE  ,3 ,"Telescope bonnette guide probe Z position"       )
+MK_INT(  530.6, GUIFLUX ,   "TCS guider flux"                                 )
+MK_PFL( 530.71, GUIFWHX ,2, "TCS guider average x FWHM in pixels"             )
+MK_PFL( 530.72, GUIFWHY ,2, "TCS guider average y FWHM in pixels"             )
+MK_INT(  530.8, SKYFLUX ,   "TCS total sky flux"                              )
+
+MK_STR(  531.1,	GUINAME1,   "TCS guider #1 identification"                    )
+MK_VAL( 531.21,	GUIEQUI1,   "TCS guider #1 equinox"                           )
+MK_STR( 531.22,	GUIRADE1,   "TCS guider #1 system for equinox"                )
+MK_STR( 531.23,	GUIRA1  ,   "TCS guider #1 right ascension"                   )
+MK_STR( 531.24,	GUIDEC1 ,   "TCS guider #1 declination"                       )
+MK_PFL( 531.25, GUIRAPM1,2, "TCS guider #1 right ascen proper motion arcsec"  )
+MK_PFL( 531.26, GUIDEPM1,2, "TCS guider #1 declination proper motion arcsec"  )
+MK_STR(  531.3, GUIOBJN1,   "TCS guider #1 object name"                       )
+MK_PFL(	 531.4,	GUIMAGN1,1, "TCS guider #1 object magnitude"                  )
+MK_PFL( 531.51, GUIPOSX1,3, "TCS guider #1 probe x position"                  )
+MK_PFL( 531.52, GUIPOSY1,3, "TCS guider #1 probe y position"                  )
+MK_PFL( 531.53, GUIPOSZ1,3, "TCS guider #1 probe z position"                  )
+MK_INT(  531.6, GUIFLUX1,   "TCS guider #1 flux"                              )
+MK_PFL( 531.71, GUIFWHX1,2, "TCS guider #1 average x FWHM in pixels"          )
+MK_PFL( 531.72, GUIFWHY1,2, "TCS guider #1 average y FWHM in pixels"          )
+
+MK_STR(  532.1,	GUINAME2,   "TCS guider #2 identification"                    )
+MK_VAL( 532.21,	GUIEQUI2,   "TCS guider #2 equinox"                           )
+MK_STR( 532.22,	GUIRADE2,   "TCS guider #2 system for equinox"                )
+MK_STR( 532.23,	GUIRA2  ,   "TCS guider #2 right ascension"                   )
+MK_STR( 532.24,	GUIDEC2 ,   "TCS guider #2 declination"                       )
+MK_PFL( 532.25, GUIRAPM2,2, "TCS guider #2 right ascen proper motion arcsec"  )
+MK_PFL( 532.26, GUIDEPM2,2, "TCS guider #2 declination proper motion arcsec"  )
+MK_STR(  532.3, GUIOBJN2,   "TCS guider #2 object name"                       )
+MK_PFL(	 532.4,	GUIMAGN2,1, "TCS guider #2 object magnitude"                  )
+MK_PFL( 532.51, GUIPOSX2,3, "TCS guider #2 probe x position"                  )
+MK_PFL( 532.52, GUIPOSY2,3, "TCS guider #2 probe y position"                  )
+MK_PFL( 532.53, GUIPOSZ2,3, "TCS guider #2 probe z position"                  )
+MK_INT(  532.6, GUIFLUX2,   "TCS guider #2 flux"                              )
+MK_PFL( 532.71, GUIFWHX2,2, "TCS guider #2 average x FWHM in pixels"          )
+MK_PFL( 532.72, GUIFWHY2,2, "TCS guider #2 average y FWHM in pixels"          )
+
+MK_STR(  533.1,	GUINAME3,   "TCS guider #3 identification"                    )
+MK_VAL( 533.21,	GUIEQUI3,   "TCS guider #3 equinox"                           )
+MK_STR( 533.22,	GUIRADE3,   "TCS guider #3 system for equinox"                )
+MK_STR( 533.23,	GUIRA3  ,   "TCS guider #3 right ascension"                   )
+MK_STR( 533.24,	GUIDEC3 ,   "TCS guider #3 declination"                       )
+MK_PFL( 533.25, GUIRAPM3,2, "TCS guider #3 right ascen proper motion arcsec"  )
+MK_PFL( 533.26, GUIDEPM3,2, "TCS guider #3 declination proper motion arcsec"  )
+MK_STR(  533.3, GUIOBJN3,   "TCS guider #3 object name"                       )
+MK_PFL(	 533.4,	GUIMAGN3,1, "TCS guider #3 object magnitude"                  )
+MK_PFL( 533.51, GUIPOSX3,3, "TCS guider #3 probe x position"                  )
+MK_PFL( 533.52, GUIPOSY3,3, "TCS guider #3 probe y position"                  )
+MK_PFL( 533.53, GUIPOSZ3,3, "TCS guider #3 probe z position"                  )
+MK_INT(  533.6, GUIFLUX3,   "TCS guider #3 flux"                              )
+MK_PFL( 533.71, GUIFWHX3,2, "TCS guider #3 average x FWHM in pixels"          )
+MK_PFL( 533.72, GUIFWHY3,2, "TCS guider #3 average y FWHM in pixels"          )
+
+MK_STR(  534.1,	GUINAME4,   "TCS guider #4 identification"                    )
+MK_VAL( 534.21,	GUIEQUI4,   "TCS guider #4 equinox"                           )
+MK_STR( 534.22,	GUIRADE4,   "TCS guider #4 system for equinox"                )
+MK_STR( 534.23,	GUIRA4  ,   "TCS guider #4 right ascension"                   )
+MK_STR( 534.24,	GUIDEC4 ,   "TCS guider #4 declination"                       )
+MK_PFL( 534.25, GUIRAPM4,2, "TCS guider #4 right ascen proper motion arcsec"  )
+MK_PFL( 534.26, GUIDEPM4,2, "TCS guider #4 declination proper motion arcsec"  )
+MK_STR(  534.3, GUIOBJN4,   "TCS guider #4 object name"                       )
+MK_PFL(	 534.4,	GUIMAGN4,1, "TCS guider #4 object magnitude"                  )
+MK_PFL( 534.51, GUIPOSX4,3, "TCS guider #4 probe x position"                  )
+MK_PFL( 534.52, GUIPOSY4,3, "TCS guider #4 probe y position"                  )
+MK_PFL( 534.53, GUIPOSZ4,3, "TCS guider #4 probe z position"                  )
+MK_INT(  534.6, GUIFLUX4,   "TCS guider #4 flux"                              )
+MK_PFL( 534.71, GUIFWHX4,2, "TCS guider #4 average x FWHM in pixels"          )
+MK_PFL( 534.72, GUIFWHY4,2, "TCS guider #4 average y FWHM in pixels"          )
+
+MK_PFL(  540.1,	AIRMASS ,3, "Airmass at start of observation"                 )
+MK_PFL(  540.2, TELALT  ,2, "Telescope altitude at start of observation, deg" )
+MK_PFL(  540.3, TELAZ   ,2, "Telescope azimuth at start, deg, 0=N 90=E 270=W" )
+MK_PFL(  540.4, MOONANGL,2, "Angle from object to moon at start in degrees"   )
+MK_STR(  550.0,	FOCUSID ,   "Telescope focus in use"                          )
+MK_STR(  550.1, TELCONF ,   "Telescope focus in use"                          )
+MK_INT(  551.0,	FOCUSPOS,   "Telescope focus encoder readout"                 )
+MK_INT(  551.1,	TELFOCUS,   "Telescope focus encoder readout"                 )
+MK_PFL(  552.0,	BONANGLE,2, "Telescope bonnette rotation angle in degrees"    )
+MK_PFL(  552.1,	ROTANGLE,2, "Telescope bonnette rotation angle in degrees"    )
+
+/*
+ * The following values perhaps should have their own section, but they
+ * are also (with some stretch) a part of TCS so for now they are here.
+ * They should only be included in MegaPrime and WIRCam images.
+ */
+
+MK_PFL(  560.1, ISUGAIN ,2, "Instrument Stabilization Unit control gain"      )
+MK_PFL(  560.2, ISURATE ,2, "ISU control rate in Hz"                          )
+MK_PFL(  560.3, ISUTCSOR,2, "ISU offload rate in Hz to telescope position"    )
+MK_STR(  560.4, ISUSTATE,   "ISU control state (Off/Only/TCS/Full/Frozen)"    )
+MK_PFL(  560.5, ISUSTDVX,2, "ISU most recent minute std dev in x in arcsec"   )
+MK_PFL(  560.6, ISUSTDVY,2, "ISU most recent minute std dev in y in arcsec"   )
+
+/*
+ * The following values perhaps should have their own section, but they
+ * are also (with some stretch) a part of TCS so for now they are here.
+ * They should only be included in MegaPrime images.
+ */
+
+MK_PFL(  570.1, FSAGAIN ,2, "Focus Stage Assembly movement control gain"      )
+MK_PFL(  570.2, FSARATE ,2, "FSA control rate in Hz"                          )
+MK_PFL(  570.3, FSATHRES,3, "FSA movement threshold in mm"                    )
+MK_STR(  570.4, FSASTATE,   "FSA control state (Off/On/Paused)"               )
+MK_INT(  570.5, FSANMOVE,   "FSA number of actual moves in last 10 minutes"   )
+MK_PFL( 570.61, FSAMINZ ,3, "FSA minimum position in last 10 minutes"         )
+MK_PFL( 570.62, FSAMAXZ ,3, "FSA maximum position in last 10 minutes"         )
+MK_PFL( 570.63, FSAMEANZ,3, "FSA mean position in last 10 minutes"            )
+MK_PFL( 570.64, FSASTDVZ,3, "FSA position std dev in last 10 minutes"         )
+
+/*
+ * The following values are used to collect data for TCS pointing model
+ * tuning.  Some of the values duplicate numbers elsewhere in the headers,
+ * but where it matters these are all read at the same time in the IOC.
+ */
+
+MK_STR( 580.11, TCSGPSBC,   "TCS GPS read out in BCD"                         )
+MK_PFL( 580.12, TCSGPSTM,5, "TCS GPS clock time in decimal hours"             )
+MK_STR( 580.13, TCSRBUSS,   "TCS RBUSS clock time"                            )
+MK_STR( 580.14, TCSEPICS,   "TCS EPICS clock time"                            )
+MK_STR( 580.15, TCSAMODE,   "TCS acquisition mode - T/O/G/g for t/o/g coords" )
+MK_PFL( 580.21, TCSMJD  ,7, "TCS MJD"                                         )
+MK_PFL( 580.22, TCSLST  ,5, "TCS LST in decimal hours"                        )
+MK_PFL( 580.31, TCSAPHA ,4, "TCS apparent hour angle in degrees"              )
+MK_PFL( 580.32, TCSAPDEC,4, "TCS apparent declination in degrees"             )
+MK_PFL( 580.41, TCSOBHA ,4, "TCS observed hour angle in degrees"              )
+MK_PFL( 580.42, TCSOBDEC,4, "TCS observed declination in degrees"             )
+MK_INT( 580.51, TCSENHA ,   "TCS hour angle encoder bits reading"             )
+MK_INT( 580.52, TCSENDEC,   "TCS declination encoder bits reading"            )
+MK_PFL( 580.61, TCSEDHA ,4, "TCS hour angle encoder in degrees"               )
+MK_PFL( 580.62, TCSEDDEC,4, "TCS declination encoder in degrees"              )
+MK_PFL( 580.71, TCSMVRA ,4, "TCS right ascension acquisition move in arcsec"  )
+MK_PFL( 580.72, TCSMVDEC,4, "TCS declination acquisition move in arcsec"      )
+MK_PFL( 580.81, TCSMVX  ,3, "TCS secondary guider acquisition x move in mm"   )
+MK_PFL( 580.82, TCSMVY  ,3, "TCS secondary guider acquisition y move in mm"   )
+
+/*
+ * Adaptive Optics : 700.000 to 799.999
+ *
+ *
+ *TYPE --IDX--  KEYWORD- PRC -------------------COMMENT----------------------*/
+
+MK_CMT(  700.1, CMTAOB1 ,   ""                                                )
+MK_CMT(  700.2, CMTAOB2 ,   "Adaptive Optics Bonnette"                        )
+MK_CMT(  700.3, CMTAOB3 ,   "------------------------"                        )
+MK_CMT(  700.4, CMTAOB4 ,   ""                                                )
+
+/* 710 through 719 are input control values to the real time system */
+
+/*
+ * state of AO correction (terms come from original description of loop
+ * operation which was deemed not PC and changed to "correction")
+ */
+MK_STR(  711.0, AOBLOOP ,   "AOB closed loop Open/Closed"                     )
+/*
+ * primary gain in the integrator providing signals to the bimorph
+ */
+MK_PFL(  712.0, LOOPGAIN,1, "Gain of the integrator"                          )
+/*
+ * correction can be fully automatic (all modes, bimorph offloading to
+ * tip/tilt mirror - nested), manual (manually selected modes, tip/tilt
+ * mode if selected only feeds tip/tilt mirror, i.e., not via bimorph
+ * - single), or tip/tilt only (bimorph not used at all, kept flat)
+ */
+MK_STR(  713.0, LOOPNES ,   "Loop type \"Nested\"/\"Tip/Tilt\"/\"Single\""    )
+/*
+ * in automatic correction this is the gain between the bimorph and the
+ * tip/tilt mirror
+ */
+MK_PFL(  714.0, LOOPNESG,1, "Nested loop gain"                                )
+/*
+ * the individual mode gains can be fixed or optimized based on signal
+ * levels
+ */
+MK_STR(  715.0, LOOPOPT ,   "Optimized loop control True/False"               )
+/*
+ * stroke of the membrane mirror (1-256)
+ */
+MK_INT(  716.0, WFSGOPT ,   "WFS optical gain"                                )
+/*
+ * integration time for each calculation cycle
+ */
+MK_PFL(  717.0, WFSSAMP ,3, "WFS sampling period (sec)"                       )
+
+/* 720-729 are measured values calculated by the real time system */
+
+/*
+ * calculated value of R0
+ */
+MK_PFL(  721.0, R0      ,1, "Fried parameter (cm)"                            )
+/*
+ * sum of the photon counts on the 19 APD's, for the most recent integration
+ */
+MK_INT(  722.0, WFSCOUNT,   "Total flux count on WFS"                         )
+
+/* 750-769 are input control values to the bench control ProLog(tm) */
+
+/*
+ * whether the atmospheric dispersion corrector (ADC) is in or out of the
+ * optical path
+ */
+MK_STR(  751.0, ADCPOS  ,   "AOB ADC position In/Out"                         )
+/*
+ * angle of the dispersion caused by the ADC
+ */
+MK_PFL(  752.0, ADCANGLE,2, "AOB ADC angle (degrees)"                         )
+/*
+ * amount of dispersion caused by the relative angle between the ADC prisms
+ */
+MK_PFL(  753.0, ADCPOWER,4, "AOB ADC power"                                   )
+/*
+ * beam splitter id code read from slugs on the splitters
+ */
+MK_INT(  754.0, BEAMSPID,   "Beam splitter ID"                                )
+/*
+ * beam splitter description based on the above codes
+ */
+MK_STR(  755.0, BEAMSP  ,   "Beam splitter description"                       )
+/*
+ * whether the AOB central mirror is in (AO system in light path)
+ * or out (AO system not being used)
+ */
+MK_STR(  756.0, MIRSLIDE,   "AOB mirror slide \"F/20\"/\"F/8\""               )
+/*
+ * neutral density filter position (in optical path to WFS)
+ *   0 == home
+ *   1 == no filter (close but not quite the same physical position as home)
+ *   2 == 1.0 density filter
+ *   3 == 2.0    "      "
+ *   4 == 3.0    "      "
+ */
+MK_INT(  757.0, WFSNDID ,   "WFS ND filter position"                          )
+/*
+ * following 3 are the Wave Front Sensor (WFS) position in a coordinate
+ * system close to having z parallel to the optical path
+ */
+MK_PFL(  761.0, WFSX    ,1, "Translated WFS X coord (steps)"                  )
+MK_PFL(  762.0, WFSY    ,1, "Translated WFS Y coord (steps)"                  )
+MK_PFL(  763.0, WFSZ    ,1, "Translated WFS Z coord (steps)"                  )
+/*
+ * Calibration Sources : 900.000 to 999.999
+ *
+ *
+ *TYPE --IDX--  KEYWORD- PRC -------------------COMMENT----------------------*/
+
+MK_CMT(  900.1, CMTCAL1 ,   ""                                                )
+MK_CMT(  900.2, CMTCAL2 ,   "Calibration sources"                             )
+MK_CMT(  900.3, CMTCAL3 ,   "-------------------"                             )
+MK_CMT(  900.4, CMTCAL4 ,   ""                                                )
+
+/* 910 - 919 are flat field lamps */
+
+/*
+ * Gecko lamps are computer controlled, these give On/Off status and
+ * intensity
+ */
+MK_STR(  910.0, FFLAMPON,   "flatfield lamp status ON/OFF"                    )
+MK_INT(  911.0, FFLAMP  ,   "flatfield lamp intensity"                        )
+/* add which lamp if get control of dome flat fields */
+
+/* 920-999 are calibration lamp sources */
+
+/*
+ * Gecko has 4 lamps, these give On/Off status and descriptions
+ */
+MK_STR(  920.0, CLAMP0ON,   "comparison lamp 0 status ON/OFF"                 )
+MK_STR(  920.1, CLAMP0  ,   "comparison lamp 0 description"                   )
+MK_STR(  921.0, CLAMP1ON,   "comparison lamp 1 status ON/OFF"                 )
+MK_STR(  921.1, CLAMP1  ,   "comparison lamp 1 description"                   )
+MK_STR(  922.0, CLAMP2ON,   "comparison lamp 2 status ON/OFF"                 )
+MK_STR(  922.1, CLAMP2  ,   "comparison lamp 2 description"                   )
+MK_STR(  923.0, CLAMP3ON,   "comparison lamp 3 status ON/OFF"                 )
+MK_STR(  923.1, CLAMP3  ,   "comparison lamp 3 description"                   )
+
+/*
+ * GumBall has 10 lamps, though the last two are Fabre-Perot lamps sharing
+ * the same light path and cannot be on together, these give On/Off status
+ * for each
+ */
+MK_STR(  930.0, CALIBL0 ,   "lamp 0 ON/OFF"                                   )
+MK_STR(  931.0, CALIBL1 ,   "lamp 1 ON/OFF"                                   )
+MK_STR(  932.0, CALIBL2 ,   "lamp 2 ON/OFF"                                   )
+MK_STR(  933.0, CALIBL3 ,   "lamp 3 ON/OFF"                                   )
+MK_STR(  934.0, CALIBL4 ,   "lamp 4 ON/OFF"                                   )
+MK_STR(  935.0, CALIBL5 ,   "lamp 5 ON/OFF"                                   )
+MK_STR(  936.0, CALIBL6 ,   "lamp 6 ON/OFF"                                   )
+MK_STR(  937.0, CALIBL7 ,   "lamp 7 ON/OFF"                                   )
+MK_STR(  938.0, CALIBL8 ,   "lamp 8 ON/OFF"                                   )
+MK_STR(  939.0, CALIBL9 ,   "lamp 9 ON/OFF"                                   )
+
+/*
+ * Instrument : 1000.000 to 1999.999
+ *
+ *TYPE --IDX--  KEYWORD- PRC -------------------COMMENT----------------------*/
+
+MK_CMT( 1000.1, CMTINST1,   ""                                                )
+MK_CMT( 1000.2, CMTINST2,   "Instrument Description"                          )
+MK_CMT( 1000.3, CMTINST3,   "----------------------"                          )
+MK_CMT( 1000.4, CMTINST4,   ""                                                )
+
+/*
+ * There are lots of shared keywords among the instruments and spectrographs.
+ * Where the keywords are really general purpose (e.g., FILTER) they are
+ * separated from the instrument areas.  Where the keywords are more specific,
+ * they are associated with the "first" (arbitrarily defined as starting about
+ * 1992 :-) instrument that used them.
+ */
+
+/* 1010 through 1099 are filter wheel info */
+
+/*
+ * Somewhere in the optical train there be filters.  If there is one filter
+ * use the FILTER sequence, whether it is actually in the detector or the
+ * instrument.  If there are two filters (usual case for IR), use the WHEEL
+ * sequence.  For an IR camera on an instrument with a filter, use both.
+ */
+
+/*
+ * code descriptions for all wheel identifiers
+ *
+ *  ID - numeric position number of wheel
+ *  <null> or DE - text description of filter mounted in that position
+ *  LB - lower bound of filter transmission - units Angstrom or microns?
+ *  UB - upper bound of filter transmission - units Angstrom or microns?
+ *  BW - bandwidth of filter transmission - units Angstrom or microns?
+ *  WL - center wave length of filter transmission - units Angstrom or microns?
+ */
+
+/* single filter wheel */
+MK_INT( 1010.1, FILTERID,   "wheel position"                                  )
+MK_STR( 1010.2, FILTER  ,   "description of filter"                           )
+MK_FLT( 1010.3, FILTERBW,3, "filter bandwidth"                                )
+MK_FLT( 1010.4, FILTERWL,3, "filter wavelength"                               )
+MK_FLT( 1010.5, FILTERLB,5, "lower bound of filter (units/%?)"                )
+MK_FLT( 1010.6, FILTERUB,5, "upper bound of filter (units/%?)"                )
+/* MOS/SIS have coded filter wheels, this is the code for the installed one */
+MK_INT( 1010.7, FILTSLID,   "filter drawer number"                            )
+
+/* first filter wheel of two */
+MK_INT( 1020.1, WHEELAID,   "'wheel' A position"                              )
+MK_STR( 1020.2, WHEELADE,   "description of filter"                           )
+MK_FLT( 1020.5, WHEELALB,4, "lower bound of filter A (units/%?)"              )
+MK_FLT( 1020.6, WHEELAUB,4, "upper bound of filter A (units/%?)"              )
+/* second filter wheel of two */
+MK_INT( 1030.1, WHEELBID,   "'wheel' B position"                              )
+MK_STR( 1030.2, WHEELBDE,   "description of filter"                           )
+MK_FLT( 1030.5, WHEELBLB,4, "lower bound of filter B (units/%?)"              )
+MK_FLT( 1030.6, WHEELBUB,4, "upper bound of filter B (units/%?)"              )
+
+/* 1100 through 1199 are general instrument set up values */
+
+/*
+ * For instruments that have an internal focus adjustment, this records
+ * the current position
+ */
+MK_FLT( 1101.0, INSTFOC ,7, "internal instrument focus value"                 )
+
+
+/*
+ * Spectrograph : 4000.000 to 4999.999
+ *
+ *TYPE --IDX--  KEYWORD- PRC -------------------COMMENT----------------------*/
+
+MK_CMT( 4000.1, CMTSPEC1,   ""                                                )
+MK_CMT( 4000.2, CMTSPEC2,   "Spectrograph Description"                        )
+MK_CMT( 4000.3, CMTSPEC3,   "------------------------"                        )
+MK_CMT( 4000.4, CMTSPEC4,   ""                                                )
+
+/* 4100 through 4199 are MOS/SIS */
+
+/*
+ * There are a grism wheel and a mask slide on each side.  By analogy with
+ * the filter wheel names:
+ *  ID     - numeric position number of wheel or slide
+ *  <null> - text description of filter mounted in that position
+ *  SLID   - the grism wheels and mask slides have codes, this is the
+ *           number of the installed wheel or slide
+ */
+MK_INT( 4101.0, GRISMID ,   "grism position"                                  )
+MK_STR( 4102.0, GRISM   ,   "grism description"                               )
+MK_INT( 4103.0, GRISSLID,   "grism drawer number"                             )
+MK_INT( 4111.0, MASKID  ,   "mask position"                                   )
+MK_STR( 4112.0, MASK    ,   "mask description"                                )
+MK_INT( 4113.0, MASKSLID,   "mask slider number"                              )
+
+/* 4200 through 4299 are GECKO */
+
+/*
+ * Description of the grating installed
+ */
+MK_STR( 4201.0, DISPEL  ,   "grating description"                             )
+/*
+ * Dispersion axis used, always 2 now
+ */
+MK_INT( 4202.0, DISPAXIS,   "dispersion axis (1 or 2)"                        )
+/*
+ * Incidence angle
+ */
+MK_FLT( 4203.0, DISPANG ,6, "grating angle in degrees"                        )
+/*
+ * Resulting center wavelength
+ */
+MK_FLT( 4204.0, WAVELENG,10,"central wavelength in angstroms"                 )
+/*
+ * In this order
+ */
+MK_INT( 4205.0, ORDER   ,   "spectral order"                                  )
+/*
+ * Magic number
+ */
+MK_FLT( 4206.0, GRSETUP ,5, "grating setup constant in degrees"               )
+/*
+ * Another magic number
+ */
+MK_FLT( 4207.0, OPENANG2,7, "setup const in degrees (opening angle / 2)"      )
+/*
+ * Why "grism angle" if it's the angle between 2 prisms ???
+ */
+MK_FLT( 4208.0, GRISMANG,5, "rel.angle between prisms in degrees"             )
+
+/*
+ * unimplemented description of slicer used
+ */
+MK_STR( 4210.0, SLICER  ,   "image slicer in place"                           )
+
+/*
+ * OPEN or CLOSED for each hartman mask
+ */
+MK_STR( 4221.0, HART1A  ,   "hartman mask 1a position OPEN/CLOSED"            )
+MK_STR( 4221.1, HART1B  ,   "hartman mask 1b position OPEN/CLOSED"            )
+MK_STR( 4222.0, HART2   ,   "hartman mask 2 position OPEN/CLOSED"             )
+MK_STR( 4223.0, HART3   ,   "hartman mask 3 position OPEN/CLOSED"             )
+MK_STR( 4224.0, HART4   ,   "hartman mask 4 position OPEN/CLOSED"             )
+
+/*
+ * unimplemented description of train used
+ * could be UV/RED/CAFE
+ */
+MK_STR( 4230.0, COUDETRN,   "coude train color UV/RED/CAFE"                   )
+
+/*
+ * unimplemented exposure meter info
+ */
+MK_STR( 4240.0, EMFILTER,   "exposure meter filter description"               )
+MK_INT( 4241.0, EMCNTS  ,   "exposure meter counts at end"                    )
+MK_INT( 4242.0, MIDEXPTM,   "mid-exposure time (seconds)"                     )
+
+/*
+ * CAFE mode added an additional card indicating that the Coude train was
+ * not being used - this ought to be added to Gecko files with NOTUSED
+ * as the value
+ */
+MK_STR( 4290.0, CAFE    ,   "CAFE is being used Null/INUSE"                   )
+
+
+/* 4300 through 4399 are for Fabre-Perot etalons */
+
+/*
+ * Description of the etalon in use
+ */
+MK_STR( 4301.0, AUXINST ,   "fabry perot etalon description"                  )
+/*
+ * Number used to figure something
+ */
+MK_PFL( 4302.0, CONST   ,2, "fabry perot constant"                            )
+/*
+ * Number of scan positions
+ */
+MK_INT( 4303.0, NUMCHAN ,   "number of channels per scan"                     )
+/*
+ * Current scan position
+ */
+MK_INT( 4304.0, CURCHAN ,   "current channel"                                 )
+/*
+ * Actual control value sent to etalon for this position
+ */
+MK_INT( 4305.0, BINVAL  ,   "binary control value"                            )
+
+/*
+ * GriF needed a bunch more values so some of these are duplicates :-(
+ */
+/*
+ * GriF adds a Fabre-Perot etalon between AOB and KIR.  This value indicates
+ * the position of the slide holding the etalon.
+ */
+MK_STR( 4320.0, GRFPPOS ,   "GriF position of FP carriage - In/Out"           )
+/*
+ * Etalon calibration values, order and standard wavelength, and BCV at
+ * standard wavelength
+ */
+MK_FLT( 4321.1, GRWAVORD,6, "GriF wavelength of order used to scan"           )
+MK_FLT( 4321.2, GRWAVCAL,6, "GriF wavelength of calibration spectral line"    )
+MK_FLT( 4321.3, GRBCVCAL,6, "GriF BCV at calibration line"                    )
+/*
+ * This records what the driver for the scan is
+ *   bcv      - specific BCV positions given
+ *   wave     - a wavelength range given and BCV's calculated
+ *   velocity - a range of delta velocities given and BCV's calculated
+ */
+MK_STR( 4322.0, GRSCANTY,   "GriF type of scan - bcv, wave, velocity"         )
+/*
+ * Wavelength values for scan, beginning, delta, and current
+ */
+MK_FLT( 4323.1, GRWAVBEG,6, "GriF wavelength at beginning of scan"            )
+MK_FLT( 4323.2, GRWAVSTP,6, "GriF wavelength step"                            )
+MK_FLT( 4323.3, GRWAVCUR,6, "GriF current observed wavelength"                )
+/*
+ * BCV equivalents (well, these are calculated from the desired wavelengths
+ * initially, but since the etalon is integral, the current wavelength value
+ * above is a conversion back from the BCV position)
+ */
+MK_FLT( 4324.1, GRBCVBEG,6, "GriF Binary Contol Value at beginning of scan"   )
+MK_FLT( 4324.2, GRBCVSTP,6, "GriF BCV step"                                   )
+MK_INT( 4324.3, GRBCVCUR,   "GriF applied BCV"                                )
+/*
+ * The scan itself is a sequence of etalon positions rounded from the
+ * desired wavelength range and step.  These are the count and current
+ * position in the scan.  (How is GRCURRCH different from GRBCVCUR ??? )
+ */
+MK_INT( 4325.1, GRNBCHAN,   "GriF number of channels in scan"                 )
+MK_INT( 4325.2, GRCURRCH,   "GriF current channel"                            )
+/*
+ * GriF also controls the plate parallelism.  These record the x and y
+ * commands
+ */
+MK_INT( 4326.1, GRBCV_X ,   "GriF X Binary Control Value FP"                  )
+MK_INT( 4326.2, GRBCV_Y ,   "GriF Y Binary Control Value FP"                  )
+
+/*
+ * Environment : 9000.000 to 9999.999
+ *
+ *TYPE --IDX--  KEYWORD- PRC -------------------COMMENT----------------------*/
+/*
+ * Data logger probe readings.  NOTE: The first two columns of the comment
+ * for these make up the authoritative probe numbers.  "loggerh" MUST BE
+ * RECOMPILED to have these changes take effect.  It takes whatever is
+ * currently in the first two columns of the COMMENT and adds /p/logger/.
+ */
+MK_PFL( 9101.0, TESPMIRE,2, "03 temp, surface, primary mirror east deg C"     )
+MK_PFL( 9102.0, TESPMIRW,2, "02 temp, surface, primary mirror west deg C"     )
+MK_PFL( 9103.0, TESPMIRS,2, "01 temp, surface, primary mirror west side degC" )
+MK_PFL( 9104.0, TEAPMCLW,2, "54 temp, air, primary mirror cell west deg C"    )
+MK_PFL( 9105.0, TEAMIRCI,2, "58 temp, air, mirror cooling in at unit deg C"   )
+MK_PFL( 9106.0, TEAMIRCO,2, "27 temp, air, mirror cooling out at cell deg C"  )
+MK_PFL( 9107.0, TEAPMSPN,2, "65 temp, air, mirror spigot north cass deg C"    )
+MK_PFL( 9108.0, TEAPMSPS,2, "64 temp, air, mirror spigot nouth M3 deg C"      )
+MK_PFL( 9109.0, TEATRNGE,2, "23 temp, air, top ring east deg C"               )
+MK_PFL( 9110.0, TEATRNGW,2, "06 temp, air, top ring west deg C"               )
+MK_PFL( 9111.0, TEANRLSB,2, "19 temp, air, north rail support beam deg C"     )
+MK_PFL( 9112.0, TESHRSET,2, "08 temp, surface, horseshoe east top deg C"      )
+MK_PFL( 9113.0, TESTELTL,2, "49 temp, surface, telescope truss low deg C"     )
+MK_PFL( 9114.0, TESTELTH,2, "52 temp, surface, telescope truss high deg C"    )
+MK_PFL( 9115.0, TEALOWWS,2, "38 temp, air, dome lower weather stat side degC" )
+MK_PFL( 9116.0, TEATOPWS,2, "36 temp, air, dome top weather stat side deg C"  )
+MK_PFL( 9117.0, TEATOPOP,2, "37 temp, air, dome top opposite weath side degC" )
+MK_PFL( 9118.0, TEA2INCH,2, "45 temp, air, two inches above fifth floor degC" )
+MK_PFL( 9119.0, TEA2INEB,2, "53 temp, air, two inches up by electronics degC" )
+MK_PFL( 9120.0, TEA6FOOT,2, "43 temp, air, six feet above fifth floor deg C"  )
+MK_PFL( 9121.0, TESCONRM,2, "61 temp, surface, floor above control room degC" )
+MK_PFL( 9122.0, TESPIERN,2, "59 temp, surface, floor by north pier deg C"     )
+MK_PFL( 9123.0, TESPIERS,2, "60 temp, surface, floor by south pier deg C"     )
+MK_PFL( 9124.0, TEAWTHRT,2, "35 temp, air, weathertron deg C"                 )
+MK_PFL( 9125.0, TEMPERAT,2, "86 temp, air, weather tower deg C"               )
+MK_PFL( 9126.0, WINDSPED,2, "84 wind speed, weather tower knots"              )
+MK_PFL( 9127.0, WINDDIR ,2, "85 wind direction, weather tower deg (N=0 E=90)" )
+MK_PFL( 9128.0, RELHUMID,2, "87 relative humidity, weather tower %"           )
+MK_PFL( 9129.0, PRESSURE,2, "31 barometric pressure, control room mb"         )
+
+/*
+ * Queue Scheduled Observing : 11000.000 to 11999.999
+ *
+ *TYPE --IDX--  KEYWORD- PRC -------------------COMMENT----------------------*/
+
+/*
+ * Processing Pipeline : 15000.000 to 15999.999
+ *
+ *TYPE --IDX--  KEYWORD- PRC -------------------COMMENT----------------------*/
+
+MK_CMT(15000.1, CMTPROC1,   ""                                                )
+MK_CMT(15000.2, CMTPROC2,   "Processing Pipeline"                             )
+MK_CMT(15000.3, CMTPROC3,   "-------------------"                             )
+MK_CMT(15000.4, CMTPROC4,   ""                                                )
+MK_STR(15101.0, CRUNID,     "Elixir camera run ID"                            )
+/*
+ * Archive System : 90000.000 to 99999.999
+ *
+ *TYPE --IDX--  KEYWORD- PRC -------------------COMMENT----------------------*/
+
+#endif /* !_INCLUDED_fh_registry */
Index: /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfh/fh_registry.h.new
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfh/fh_registry.h.new	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfh/fh_registry.h.new	(revision 23594)
@@ -0,0 +1,719 @@
+/*                                             -*- c-file-style: "Ellemtel" -*-
+
+`fh_registry.h' - A registry of FITS keywords in use at CFHT.
+
+This file is part of version 1 of the FITS Handling Library.
+Read the `License' file for terms of use and distribution.
+Copyright 2001, Canada-France-Hawaii Telescope, daprog@cfht.hawaii.edu.
+
+___This header automatically generated from `Index'. Do not edit it here!___ */
+
+/*
+ * $Id: fh_registry.h,v 1.4 2002/02/09 04:01:51 thomas Exp isani $
+ *
+ * Documentation: See http://software.cfht.hawaii.edu/fh_registry/
+ */
+
+#ifndef _INCLUDED_fh_registry
+#define _INCLUDED_fh_registry 1
+
+#if defined(__GNUC__) && defined(__STDC__)
+static const char fh_registry_rcs_id[] __attribute__ ((__unused__)) = "@(#) $Id: fh_registry.h,v 1.4 2002/02/09 04:01:51 thomas Exp isani $" ;
+#else
+static const char fh_registry_rcs_id[] = "@(#) $Id: fh_registry.h,v 1.4 2002/02/09 04:01:51 thomas Exp isani $";
+#endif
+
+/*
+ * These are environment variables which the CFHT "ccd" process (both
+ * script and older C program) make available to instrument handler programs.
+ */
+#define ENV_FFTEMPLATE	"FFTEMPLATE"
+#define ENV_OBSTYPE	"OBSTYPE"
+#define ENV_INTTIME	"INTTIME"
+#define ENV_EXPNUM	"EXPNUM"
+#define ENV_SEQNUM	"SEQNUM"
+
+/*
+ * This macro is used inside the other macros (ID_FLT, ID_INT, etc.)
+ * so that the keyword names can be referred to as fh_kw_FOO
+ * This can be used as an identifier to fh_get() or any other function
+ * instead of using the string constant "FOO".
+ */
+
+#define ID_FLT(idx, name, keyword, prec, comment) \
+static inline void \
+fh_set_##name (HeaderUnit hu, double value) \
+{ fh_set_flt(hu, idx, keyword, value, prec, comment); } \
+static inline void \
+fh_setcmt_##name (HeaderUnit hu, double value, const char* new_comment) \
+{ fh_set_flt(hu, idx, keyword, value, prec, new_comment); } \
+static inline void \
+fh_get_##name (HeaderUnit hu, double* value) \
+{ fh_get_flt(hu, keyword, value); } \
+static inline const char* \
+fh_kw_##name(void) { return keyword; }
+
+#define ID_INT(idx, name, keyword, comment) \
+static inline void \
+fh_set_##name (HeaderUnit hu, int value) \
+{ fh_set_int(hu, idx, keyword, value, comment); } \
+static inline void \
+fh_setcmt_##name (HeaderUnit hu, int value, const char* new_comment) \
+{ fh_set_int(hu, idx, keyword, value, new_comment); } \
+static inline void \
+fh_get_##name (HeaderUnit hu, int* value) \
+{ fh_get_int(hu, keyword, value); } \
+static inline const char* \
+fh_kw_##name(void) { return keyword; }
+
+#define ID_BLN(idx, name, keyword, comment) \
+static inline void \
+fh_set_##name (HeaderUnit hu, fh_bool value) \
+{ fh_set_bool(hu, idx, keyword, value, comment); } \
+static inline void \
+fh_setcmt_##name (HeaderUnit hu, fh_bool value, const char* new_comment) \
+{ fh_set_bool(hu, idx, keyword, value, new_comment); } \
+static inline void \
+fh_get_##name (HeaderUnit hu, fh_bool* value) \
+{ fh_get_bool(hu, keyword, value); } \
+static inline const char* \
+fh_kw_##name(void) { return keyword; }
+
+#define ID_STR(idx, name, keyword, comment) \
+static inline void \
+fh_set_##name (HeaderUnit hu, const char* value) \
+{ fh_set_str(hu, idx, keyword, value, comment); } \
+static inline void \
+fh_setcmt_##name (HeaderUnit hu, const char* value, const char* new_comment) \
+{ fh_set_str(hu, idx, keyword, value, new_comment); } \
+static inline void \
+fh_get_##name (HeaderUnit hu, char* value, int maxlen) \
+{ fh_get_str(hu, keyword, value, maxlen); } \
+static inline const char* \
+fh_kw_##name(void) { return keyword; }
+
+#define ID_VAL(idx, name, keyword, comment) \
+static inline void \
+fh_set_##name (HeaderUnit hu, const char* value) \
+{ fh_set_val(hu, idx, keyword, value, comment); } \
+static inline void \
+fh_setcmt_##name (HeaderUnit hu, const char* value, const char* new_comment) \
+{ fh_set_val(hu, idx, keyword, value, new_comment); } \
+static inline const char* \
+fh_kw_##name(void) { return keyword; }
+
+#define ID_CMT(idx, name, comment) \
+static inline void \
+fh_set_##name (HeaderUnit hu) \
+{ fh_set_com(hu, idx, "COMMENT", comment); }
+
+/*
+ * Now the auto-stringified versions of ID_*() ...
+ */
+#define MK_FLT(idx, name, prec, comment) ID_FLT(idx, name, #name, prec, comment)
+#define MK_INT(idx, name, comment) ID_INT(idx, name, #name, comment)
+#define MK_STR(idx, name, comment) ID_STR(idx, name, #name, comment)
+#define MK_VAL(idx, name, comment) ID_VAL(idx, name, #name, comment)
+#define MK_BLN(idx, name, comment) ID_BLN(idx, name, #name, comment)
+#define MK_CMT(idx, name, comment) ID_CMT(idx, name, comment)
+
+/*
+ * FITS Structure : 0.000 to 9.999
+ *
+ * The following keywords give basic information about the structure and
+ * dimensions of the FITS file, and must appear in the specific order
+ * below, usually without any other intervening keywords.
+ *
+ *TYPE --IDX--  KEYWORD- PRC -------------------COMMENT----------------------*/
+MK_BLN( 0.0,	SIMPLE  ,   "Standard FITS"                                   )
+MK_STR( 0.0,	XTENSION,   ""                                                )
+MK_INT( 1.0,	BITPIX  ,   "Bits per pixel"                                  )
+MK_INT( 2.0,	NAXIS   ,   "Number of axes"                                  )
+MK_INT( 2.1,	NAXIS1  ,   "Number of pixel columns"                         )
+MK_INT( 2.2,	NAXIS2  ,   "Number of pixel rows"                            )
+MK_INT( 2.3,	NAXIS3  ,   "Number of stacked frames (cube)"                 )
+MK_BLN( 3.0,	EXTEND  ,   "File contains extensions"                        )
+MK_INT( 3.1,	NEXTEND ,   "Number of extensions"                            )
+MK_BLN( 4.0,	GROUPS  ,   "File contains random groups records"             )
+MK_INT( 5.0,	PCOUNT  ,   "Random parameters before each array in a group"  )
+MK_INT( 6.0,	GCOUNT  ,   "Number of random groups"                         )
+MK_INT( 7.0000,	TFIELDS ,   "Number of fields in a row"                       )
+MK_STR( 7.0011,	TFORM1  ,   "Table format for field 1"                        )
+MK_INT( 7.0012,	TBCOL1  ,   "Start Column for field 1"                        )
+MK_STR( 7.0021,	TFORM2  ,   "Table format for field 2"                        )
+MK_INT( 7.0022,	TBCOL2  ,   "Start Column for field 2"                        )
+MK_STR( 7.0031,	TFORM3  ,   "Table format for field 3"                        )
+MK_INT( 7.0032,	TBCOL3  ,   "Start Column for field 3"                        )
+MK_STR( 7.0041,	TFORM4  ,   "Table format for field 4"                        )
+MK_INT( 7.0042,	TBCOL4  ,   "Start Column for field 4"                        )
+MK_STR( 7.0051,	TFORM5  ,   "Table format for field 5"                        )
+MK_INT( 7.0052,	TBCOL5  ,   "Start Column for field 5"                        )
+
+/*
+ * Summary : 50.000 to 59.999
+ *
+ *TYPE --IDX--  KEYWORD- PRC -------------------COMMENT----------------------*/
+MK_CMT(   50.1, CMTSUM1 ,   ""                                                )
+MK_CMT(   50.2, CMTSUM2 ,   "Observation Summary"                             )
+MK_CMT(   50.3, CMTSUM3 ,   "-------------------"                             )
+MK_CMT(   50.4, CMTSUM4 ,   ""                                                )
+MK_CMT(   50.5, CMTSUM5 ,   ""                                                )
+MK_STR(   51.0, CMMTOBS ,   ""                                                )
+MK_STR(   52.0, CMMTSEQ ,   ""                                                )
+MK_STR(   53.0, OBJECT  ,   ""                                                )
+MK_STR(   54.0, OBSERVER,   ""                                                )
+MK_STR(   55.0, PI_NAME ,   ""                                                )
+MK_STR(   56.0, RUNID   ,   ""                                                )
+
+/*
+ * General : 70.000 to 79.999
+ *
+ *TYPE --IDX--  KEYWORD- PRC -------------------COMMENT----------------------*/
+MK_CMT(   70.1, CMTGEN1 ,   ""                                                )
+MK_CMT(   70.2, CMTGEN2 ,   "General"                                         )
+MK_CMT(   70.3, CMTGEN3 ,   "-------"                                         )
+MK_CMT(   70.4, CMTGEN4 ,   ""                                                )
+MK_STR(   71.0, FILENAME,   "Base filename at acquisition"                    )
+MK_STR(   71.1, EXTNAME ,   "Extension name"                                  )
+MK_INT(   71.2, EXTVER  ,   "Extension version"                               )
+MK_STR(   74.0, DATE    ,   "UTC Date of file creation"                       )
+MK_STR(   74.1, HSTTIME ,   "Local time in Hawaii"                            )
+MK_STR(   75.0, ORIGIN  ,   "Canada-France-Hawaii Telescope"                  )
+MK_STR(   77.0, IMAGESWV,   "Image creation software version"                 )
+
+/*
+ * Detector : 100.000 to 199.999
+ *
+ *TYPE --IDX--  KEYWORD- PRC -------------------COMMENT----------------------*/
+MK_CMT(  100.1,	CMTDET1	,   ""                                                )
+MK_CMT(  100.2,	CMTDET2	,   "Detector"                                        )
+MK_CMT(  100.3,	CMTDET3	,   "--------"                                        )
+MK_CMT(  100.4,	CMTDET4	,   ""                                                )
+MK_STR(  101.0,	DETECTOR,   ""                                                )
+MK_STR(  102.0, DETSIZE	,   "Total data pixels in full mosaic"                )
+MK_STR(  103.0,	RASTER	,   ""                                                )
+MK_STR(  105.0,	CCDSUM	,   "Binning factors"                                 )
+MK_INT(  105.1,	CCDBIN1	,   "Binning factor along first axis"                 )
+MK_INT(  105.2,	CCDBIN2	,   "Binning factor along second axis"                )
+MK_FLT(  110.0,	PIXSIZE ,3, "Pixel size for both axes (microns)"              )
+MK_FLT(  111.1,	PIXSIZE1,3, "Pixel size for axis 1 (microns)"                 )
+MK_FLT(  111.2,	PIXSIZE2,3, "Pixel size for axis 2 (microns)"                 )
+MK_FLT(  112.1,	PIXSCAL1,4, "Pixel scale for axis 1 (arcsec/pixel)"           )
+MK_FLT(  112.2,	PIXSCAL2,4, "Pixel scale for axis 2 (arcsec/pixel)"           )
+MK_STR(  130.0,	AMPLIST	,   "List of amplifiers"                              )
+MK_STR(  131.0,	AMPNAME	,   "Amplifier name"                                  )
+MK_STR(  140.0, CONSWV	,   "Controller software DSPID and SERNO versions"    )
+MK_STR(  150.0,	DETSTAT	,   "Detector status"                                 )
+MK_FLT(  150.1, DETTEM  ,3, "Detector temperature"                            )
+MK_INT(  152.0, MAXLIN  ,   "Maximum linearity value (ADU)"                   )
+MK_INT(  152.1, SATURATE,   "Saturation value (ADU)"                          )
+MK_FLT(  155.0,	GAIN    ,3, "Amplifier gain (electrons/ADU)"                  )
+MK_FLT(  156.0,	RDNOISE ,3, "Read noise (electrons)"                          )
+MK_FLT(  157.0, DCURRENT,5, "Dark current (ADU/pixel/second)"                 )
+
+/* ****************************************************
+ * *** World Coordinate System : 300.000 to 399.999 ***
+ * ****************************************************
+ *
+ *TYPE --IDX--  KEYWORD- PRC -------------------COMMENT----------------------*/
+
+/*
+ * Telescope : 500.000 to 599.999
+ *
+ * Almost all keywords we define can be generated with the MK_XXX macros.
+ * The only exceptions are those keywords which contain a '-', since the
+ * '-' is not a legal character in a C identifier.  TCS uses four of them
+ * (DATE-OBS, UTC-OBS, MJD-OBS, and LST-OBS.)  The ID_XXX macros are used
+ * and the '-' is translated to a '_' (underscore) for use in C programs.
+ *
+ * NOTE: TELESCOP, TELSTAT, TELCONF, LATITUDE, and LONGITUD are currently
+ * inserted by DetCom and _not_ tcsh.  This should probably be changed.
+ *
+ *TYPE --IDX--  SYMBOL--  KEYWORD-  -----------COMMENT----------------------*/
+ID_STR( 511.1,	DATE_OBS,"DATE-OBS", "Date at start of observation (UTC)"     )
+ID_STR( 512.1,	UTC_OBS ,"UTC-OBS", "Time at start of observation (UTC)"      )
+ID_FLT( 513.1,MJD_OBS,"MJD-OBS",12,"Modified Julian Date at start of obs."    )
+ID_STR( 514.1,	LST_OBS ,"LST-OBS", "Sidereal time at start of exposure"      )
+
+/*TYPE --IDX--  KEYWORD- PRC -------------------COMMENT----------------------*/
+MK_CMT(  500.1, CMTTCS1 ,   ""                                                )
+MK_CMT(  500.2, CMTTCS2 ,   "Telescope"                                       )
+MK_CMT(  500.3, CMTTCS3 ,   "---------"                                       )
+MK_CMT(  500.4, CMTTCS4 ,   ""                                                )
+MK_STR(  501.0, TELESCOP,   ""                                                )
+MK_FLT(  502.1, LATITUDE,7, "Latitude (degrees N)"                            )
+MK_FLT(  502.2, LONGITUD,7, "Longitude (degrees E)"                           )
+MK_STR(  503.0, TELSTAT ,   "Telescope Control System status"                 )
+MK_STR(  510.0, TIMESYS ,   "Time System for DATExxxx"                        )
+MK_STR(  512.0,	UTIME   ,   "Time at start of observation (UT)"               )
+MK_FLT(  513.0,	MJDATE  ,12,"Modified Julian Date at start of observation"    )
+MK_STR(  514.0,	SIDTIME ,   "Sidereal time at start of observation"           )
+MK_VAL(  521.0,	EPOCH   ,   "Equinox of coordinates"                          )
+MK_VAL(  521.1,	EQUINOX ,   "Equinox of coordinates"                          )
+MK_STR(  522.0,	RADECSYS,   "Coordinate system for equinox (FK4/FK5/GAPPT)"   )
+MK_STR(  522.1,	RA      ,   "Object right ascension"                          )
+MK_STR(  522.2,	DEC     ,   "Object declination"                              )
+MK_FLT(  523.1,	RA_DEG  ,8, "Object right ascension in degrees"               )
+MK_FLT(  523.2,	DEC_DEG ,8, "Object declination in degrees"                   )
+MK_INT(  530.0,	NGUIDER	,   "Number of guiders"                               )
+MK_VAL(  530.2,	GUIEQUIN,   "TCS guider equinox"                              )
+MK_STR(  530.3,	GUIRADEC,   "TCS guider system for equinox"                   )
+MK_STR(  530.4,	GUIRA   ,   "TCS guider right ascension"                      )
+MK_STR(  530.5,	GUIDEC  ,   "TCS guider declination"                          )
+
+MK_STR(  531.1,	GUINAME1,   "TCS guider #1 identification"                    )
+MK_VAL(  531.2,	GUIEQUI1,   "TCS guider #1 equinox"                           )
+MK_STR(  531.3,	GUIRADE1,   "TCS guider #1 system for equinox"                )
+MK_STR(  531.4,	GUIRA1  ,   "TCS guider #1 right ascension"                   )
+MK_STR(  531.5,	GUIDEC1 ,   "TCS guider #1 declination"                       )
+MK_STR(  531.6, GUIOBJN1,   "TCS guider #1 object name"                       )
+MK_FLT(	 531.7,	GUIMAGN1,2, "TCS guider #1 object magnitude"                  )
+
+MK_STR(  532.1,	GUINAME2,   "TCS guider #2 identification"                    )
+MK_VAL(  531.0,	GUIEQUI2,   "TCS guider #2 equinox"                           )
+MK_STR(  532.0,	GUIRADE2,   "TCS guider #2 system for equinox"                )
+MK_STR(  532.1,	GUIRA2  ,   "TCS guider #2 right ascension"                   )
+MK_STR(  532.2,	GUIDEC2 ,   "TCS guider #2 declination"                       )
+MK_STR(  532.6, GUIOBJN2,   "TCS guider #2 object name"                       )
+MK_FLT(	 532.7,	GUIMAGN2,2, "TCS guider #2 object magnitude"                  )
+
+MK_STR(  533.1,	GUINAME3,   "TCS guider #3 identification"                    )
+MK_VAL(  533.2,	GUIEQUI3,   "TCS guider #3 equinox"                           )
+MK_STR(  533.3,	GUIRADE3,   "TCS guider #3 system for equinox"                )
+MK_STR(  533.4,	GUIRA3  ,   "TCS guider #3 right ascension"                   )
+MK_STR(  533.5,	GUIDEC3 ,   "TCS guider #3 declination"                       )
+MK_STR(  533.6, GUIOBJN3,   "TCS guider #3 object name"                       )
+MK_FLT(	 533.7,	GUIMAGN3,2, "TCS guider #3 object magnitude"                  )
+
+MK_STR(  534.1,	GUINAME4,   "TCS guider #4 identification"                    )
+MK_VAL(  534.2,	GUIEQUI4,   "TCS guider #4 equinox"                           )
+MK_STR(  534.3,	GUIRADE4,   "TCS guider #4 system for equinox"                )
+MK_STR(  534.4,	GUIRA4  ,   "TCS guider #4 right ascension"                   )
+MK_STR(  534.5,	GUIDEC4 ,   "TCS guider #4 declination"                       )
+MK_STR(  534.6, GUIOBJN4,   "TCS guider #4 object name"                       )
+MK_FLT(	 534.7,	GUIMAGN4,2, "TCS guider #4 object magnitude"                  )
+
+MK_FLT(  540.0,	AIRMASS ,4, "Airmass at start of observation"                 )
+MK_STR(  550.0,	FOCUSID ,   "Telescope focus in use"                          )
+MK_STR(  550.1, TELCONF ,   "Telescope focus in use"                          )
+MK_INT(  551.0,	FOCUSPOS,   "Telescope focus encoder readout"                 )
+MK_INT(  551.1,	TELFOCUS,   "Telescope focus encoder readout"                 )
+MK_FLT(  560.0,	BONANGLE,5, "Telescope bonnette rotation angle in degrees"    )
+MK_FLT(  560.1,	ROTANGLE,5, "Telescope bonnette rotation angle in degrees"    )
+MK_FLT(  561.1,	XPROBE  ,5, "Telescope bonnette guide probe X position"       )
+MK_FLT(  561.2,	YPROBE  ,5, "Telescope bonnette guide probe Y position"       )
+MK_FLT(  561.3,	ZPROBE  ,5, "Telescope bonnette guide probe Z position"       )
+
+/*
+ * Adaptive Optics : 700.000 to 799.999
+ *
+ *
+ *TYPE --IDX--  KEYWORD- PRC -------------------COMMENT----------------------*/
+
+MK_CMT(  700.1, CMTAOB1 ,   ""                                                )
+MK_CMT(  700.2, CMTAOB2 ,   "Adaptive Optics Bonnette"                        )
+MK_CMT(  700.3, CMTAOB3 ,   "------------------------"                        )
+MK_CMT(  700.4, CMTAOB4 ,   ""                                                )
+
+/* 710 through 719 are input control values to the real time system */
+
+/*
+ * state of AO correction (terms come from original description of loop
+ * operation which was deemed not PC and changed to "correction")
+ */
+MK_STR(  711.0, AOBLOOP ,   "AOB closed loop Open/Closed"                     )
+/*
+ * primary gain in the integrator providing signals to the bimorph
+ */
+MK_FLT(  712.0, LOOPGAIN,1, "Gain of the integrator"                          )
+/*
+ * correction can be fully automatic (all modes, bimorph offloading to
+ * tip/tilt mirror - nested), manual (manually selected modes, tip/tilt
+ * mode if selected only feeds tip/tilt mirror, i.e., not via bimorph
+ * - single), or tip/tilt only (bimorph not used at all, kept flat)
+ */
+MK_STR(  713.0, LOOPNES ,   "Loop type \"Nested\"/\"Tip/Tilt\"/\"Single\""    )
+/*
+ * in automatic correction this is the gain between the bimorph and the
+ * tip/tilt mirror
+ */
+MK_FLT(  714.0, LOOPNESG,1, "Nested loop gain"                                )
+/*
+ * the individual mode gains can be fixed or optimized based on signal
+ * levels
+ */
+MK_STR(  715.0, LOOPOPT ,   "Optimized loop control True/False"               )
+/*
+ * stroke of the membrane mirror (1-256)
+ */
+MK_INT(  716.0, WFSGOPT ,   "WFS optical gain"                                )
+/*
+ * integration time for each calculation cycle
+ */
+MK_FLT(  717.0, WFSSAMP ,3, "WFS sampling period (sec)"                       )
+
+/* 720-729 are measured values calculated by the real time system */
+
+/*
+ * calculated value of R0
+ */
+MK_FLT(  721.0, R0      ,1, "Fried parameter (cm)"                            )
+/*
+ * sum of the photon counts on the 19 APD's, for the most recent integration
+ */
+MK_INT(  722.0, WFSCOUNT,   "Total flux count on WFS"                         )
+
+/* 750-769 are input control values to the bench control ProLog(tm) */
+
+/*
+ * whether the atmospheric dispersion corrector (ADC) is in or out of the
+ * optical path
+ */
+MK_STR(  751.0, ADCPOS  ,   "AOB ADC position In/Out"                         )
+/*
+ * angle of the dispersion caused by the ADC
+ */
+MK_FLT(  752.0, ADCANGLE,2, "AOB ADC angle (degrees)"                         )
+/*
+ * amount of dispersion caused by the relative angle between the ADC prisms
+ */
+MK_FLT(  753.0, ADCPOWER,4, "AOB ADC power"                                   )
+/*
+ * beam splitter id code read from slugs on the splitters
+ */
+MK_INT(  754.0, BEAMSPID,   "Beam splitter ID"                                )
+/*
+ * beam splitter description based on the above codes
+ */
+MK_STR(  755.0, BEAMSP  ,   "Beam splitter description"                       )
+/*
+ * whether the AOB central mirror is in (AO system in light path)
+ * or out (AO system not being used)
+ */
+MK_STR(  756.0, MIRSLIDE,   "AOB mirror slide \"F/20\"/\"F/8\""               )
+/*
+ * neutral density filter position (in optical path to WFS)
+ *   0 == home
+ *   1 == no filter (close but not quite the same physical position as home)
+ *   2 == 1.0 density filter
+ *   3 == 2.0    "      "
+ *   4 == 3.0    "      "
+ */
+MK_INT(  757.0, WFSNDID ,   "WFS ND filter position"                          )
+/*
+ * following 3 are the Wave Front Sensor (WFS) position in a coordinate
+ * system close to having z parallel to the optical path
+ */
+MK_FLT(  761.0, WFSX    ,1, "Translated WFS X coord (steps)"                  )
+MK_FLT(  762.0, WFSY    ,1, "Translated WFS Y coord (steps)"                  )
+MK_FLT(  763.0, WFSZ    ,1, "Translated WFS Z coord (steps)"                  )
+/*
+ * Calibration Sources : 900.000 to 999.999
+ *
+ *
+ *TYPE --IDX--  KEYWORD- PRC -------------------COMMENT----------------------*/
+
+MK_CMT(  900.1, CMTCAL1 ,   ""                                                )
+MK_CMT(  900.2, CMTCAL2 ,   "Calibration sources"                             )
+MK_CMT(  900.3, CMTCAL3 ,   "-------------------"                             )
+MK_CMT(  900.4, CMTCAL4 ,   ""                                                )
+
+/* 910 - 919 are flat field lamps */
+
+/*
+ * Gecko lamps are computer controlled, these give On/Off status and
+ * intensity
+ */
+MK_STR(  910.0, FFLAMPON,   "flatfield lamp status ON/OFF"                    )
+MK_INT(  911.0, FFLAMP  ,   "flatfield lamp intensity"                        )
+/* add which lamp if get control of dome flat fields */
+
+/* 920-999 are calibration lamp sources */
+
+/*
+ * Gecko has 4 lamps, these give On/Off status and descriptions
+ */
+MK_STR(  920.0, CLAMP0ON,   "comparison lamp 0 status ON/OFF"                 )
+MK_STR(  920.1, CLAMP0  ,   "comparison lamp 0 description"                   )
+MK_STR(  921.0, CLAMP1ON,   "comparison lamp 1 status ON/OFF"                 )
+MK_STR(  921.1, CLAMP1  ,   "comparison lamp 1 description"                   )
+MK_STR(  922.0, CLAMP2ON,   "comparison lamp 2 status ON/OFF"                 )
+MK_STR(  922.1, CLAMP2  ,   "comparison lamp 2 description"                   )
+MK_STR(  923.0, CLAMP3ON,   "comparison lamp 3 status ON/OFF"                 )
+MK_STR(  923.1, CLAMP3  ,   "comparison lamp 3 description"                   )
+
+/*
+ * GumBall has 10 lamps, though the last two are Fabre-Perot lamps sharing
+ * the same light path and cannot be on together, these give On/Off status
+ * for each
+ */
+MK_STR(  930.0, CALIBL0 ,   "lamp 0 ON/OFF"                                   )
+MK_STR(  931.0, CALIBL1 ,   "lamp 1 ON/OFF"                                   )
+MK_STR(  932.0, CALIBL2 ,   "lamp 2 ON/OFF"                                   )
+MK_STR(  933.0, CALIBL3 ,   "lamp 3 ON/OFF"                                   )
+MK_STR(  934.0, CALIBL4 ,   "lamp 4 ON/OFF"                                   )
+MK_STR(  935.0, CALIBL5 ,   "lamp 5 ON/OFF"                                   )
+MK_STR(  936.0, CALIBL6 ,   "lamp 6 ON/OFF"                                   )
+MK_STR(  937.0, CALIBL7 ,   "lamp 7 ON/OFF"                                   )
+MK_STR(  938.0, CALIBL8 ,   "lamp 8 ON/OFF"                                   )
+MK_STR(  939.0, CALIBL9 ,   "lamp 9 ON/OFF"                                   )
+
+/*
+ * Instrument : 1000.000 to 1999.999
+ *
+ *TYPE --IDX--  KEYWORD- PRC -------------------COMMENT----------------------*/
+
+MK_CMT( 1000.1, CMTINST1,   ""                                                )
+MK_CMT( 1000.2, CMTINST2,   "Instrument Description"                          )
+MK_CMT( 1000.3, CMTINST3,   "----------------------"                          )
+MK_CMT( 1000.4, CMTINST4,   ""                                                )
+
+/*
+ * There are lots of shared keywords among the instruments and spectrographs.
+ * Where the keywords are really general purpose (e.g., FILTER) they are
+ * separated from the instrument areas.  Where the keywords are more specific,
+ * they are associated with the "first" (arbitrarily defined as starting about
+ * 1992 :-) instrument that used them.
+ */
+
+/* 1010 through 1099 are filter wheel info */
+
+/*
+ * Somewhere in the optical train there be filters.  If there is one filter
+ * use the FILTER sequence, whether it is actually in the detector or the
+ * instrument.  If there are two filters (usual case for IR), use the WHEEL
+ * sequence.  For an IR camera on an instrument with a filter, use both.
+ */
+
+/*
+ * code descriptions for all wheel identifiers
+ *
+ *  ID - numeric position number of wheel
+ *  <null> or DE - text description of filter mounted in that position
+ *  LB - lower bound of filter transmission - units Angstrom or microns?
+ *  UB - upper bound of filter transmission - units Angstrom or microns?
+ *  BW - bandwidth of filter transmission - units Angstrom or microns?
+ *  WL - center wave length of filter transmission - units Angstrom or microns?
+ */
+
+/* single filter wheel */
+MK_INT( 1010.1, FILTERID,   "wheel position"                                  )
+MK_STR( 1010.2, FILTER  ,   "description of filter"                           )
+MK_FLT( 1010.3, FILTERBW,3, "filter bandwidth"                                )
+MK_FLT( 1010.4, FILTERWL,3, "filter wavelength"                               )
+MK_FLT( 1010.5, FILTERLB,5, "lower bound of filter (units/%?)"                )
+MK_FLT( 1010.6, FILTERUB,5, "upper bound of filter (units/%?)"                )
+/* MOS/SIS have coded filter wheels, this is the code for the installed one */
+MK_INT( 1010.7, FILTSLID,   "filter drawer number"                            )
+
+/* first filter wheel of two */
+MK_INT( 1020.1, WHEELAID,   "'wheel' A position"                              )
+MK_STR( 1020.2, WHEELADE,   "description of filter"                           )
+MK_FLT( 1020.5, WHEELALB,4, "lower bound of filter A (units/%?)"              )
+MK_FLT( 1020.6, WHEELAUB,4, "upper bound of filter A (units/%?)"              )
+/* second filter wheel of two */
+MK_INT( 1030.1, WHEELBID,   "'wheel' B position"                              )
+MK_STR( 1030.2, WHEELBDE,   "description of filter"                           )
+MK_FLT( 1030.5, WHEELBLB,4, "lower bound of filter B (units/%?)"              )
+MK_FLT( 1030.6, WHEELBUB,4, "upper bound of filter B (units/%?)"              )
+
+/* 1100 through 1199 are general instrument set up values */
+
+/*
+ * For instruments that have an internal focus adjustment, this records
+ * the current position
+ */
+MK_FLT( 1101.0, INSTFOC ,7, "internal instrument focus value"                 )
+
+
+/*
+ * Spectrograph : 4000.000 to 4999.999
+ *
+ *TYPE --IDX--  KEYWORD- PRC -------------------COMMENT----------------------*/
+
+MK_CMT( 4000.1, CMTSPEC1,   ""                                                )
+MK_CMT( 4000.2, CMTSPEC2,   "Spectrograph Description"                        )
+MK_CMT( 4000.3, CMTSPEC3,   "------------------------"                        )
+MK_CMT( 4000.4, CMTSPEC4,   ""                                                )
+
+/* 4100 through 4199 are MOS/SIS */
+
+/*
+ * There are a grism wheel and a mask slide on each side.  By analogy with
+ * the filter wheel names:
+ *  ID     - numeric position number of wheel or slide
+ *  <null> - text description of filter mounted in that position
+ *  SLID   - the grism wheels and mask slides have codes, this is the
+ *           number of the installed wheel or slide
+ */
+MK_INT( 4101.0, GRISMID ,   "grism position"                                  )
+MK_STR( 4102.0, GRISM   ,   "grism description"                               )
+MK_INT( 4103.0, GRISSLID,   "grism drawer number"                             )
+MK_INT( 4111.0, MASKID  ,   "mask position"                                   )
+MK_STR( 4112.0, MASK    ,   "mask description"                                )
+MK_INT( 4113.0, MASKSLID,   "mask slider number"                              )
+
+/* 4200 through 4299 are GECKO */
+
+/*
+ * Description of the grating installed
+ */
+MK_STR( 4201.0, DISPEL  ,   "grating description"                             )
+/*
+ * Dispersion axis used, always 2 now
+ */
+MK_INT( 4202.0, DISPAXIS,   "dispersion axis (1 or 2)"                        )
+/*
+ * Incidence angle
+ */
+MK_FLT( 4203.0, DISPANG ,6, "grating angle in degrees"                        )
+/*
+ * Resulting center wavelength
+ */
+MK_FLT( 4204.0, WAVELENG,10,"central wavelength in angstroms"                 )
+/*
+ * In this order
+ */
+MK_INT( 4205.0, ORDER   ,   "spectral order"                                  )
+/*
+ * Magic number
+ */
+MK_FLT( 4206.0, GRSETUP ,5, "grating setup constant in degrees"               )
+/*
+ * Another magic number
+ */
+MK_FLT( 4207.0, OPENANG2,7, "setup const in degrees (opening angle / 2)"      )
+/*
+ * Why "grism angle" if it's the angle between 2 prisms ???
+ */
+MK_FLT( 4208.0, GRISMANG,5, "rel.angle between prisms in degrees"             )
+
+/*
+ * unimplemented description of slicer used
+ */
+MK_STR( 4210.0, SLICER  ,   "image slicer in place"                           )
+
+/*
+ * OPEN or CLOSED for each hartman mask
+ */
+MK_STR( 4221.0, HART1A  ,   "hartman mask 1a position OPEN/CLOSED"            )
+MK_STR( 4221.1, HART1B  ,   "hartman mask 1b position OPEN/CLOSED"            )
+MK_STR( 4222.0, HART2   ,   "hartman mask 2 position OPEN/CLOSED"             )
+MK_STR( 4223.0, HART3   ,   "hartman mask 3 position OPEN/CLOSED"             )
+MK_STR( 4224.0, HART4   ,   "hartman mask 4 position OPEN/CLOSED"             )
+
+/*
+ * unimplemented description of train used
+ * could be UV/RED/CAFE
+ */
+MK_STR( 4230.0, COUDETRN,   "coude train color UV/RED/CAFE"                   )
+
+/*
+ * unimplemented exposure meter info
+ */
+MK_STR( 4240.0, EMFILTER,   "exposure meter filter description"               )
+MK_INT( 4241.0, EMCNTS  ,   "exposure meter counts at end"                    )
+MK_INT( 4242.0, MIDEXPTM,   "mid-exposure time (seconds)"                     )
+
+/*
+ * CAFE mode added an additional card indicating that the Coude train was
+ * not being used - this ought to be added to Gecko files with NOTUSED
+ * as the value
+ */
+MK_STR( 4290.0, CAFE    ,   "CAFE is being used Null/INUSE"                   )
+
+
+/* 4300 through 4399 are for Fabre-Perot etalons */
+
+/*
+ * Description of the etalon in use
+ */
+MK_STR( 4301.0, AUXINST ,   "fabry perot etalon description"                  )
+/*
+ * Number used to figure something
+ */
+MK_FLT( 4302.0, CONST   ,2, "fabry perot constant"                            )
+/*
+ * Number of scan positions
+ */
+MK_INT( 4303.0, NUMCHAN ,   "number of channels per scan"                     )
+/*
+ * Current scan position
+ */
+MK_INT( 4304.0, CURCHAN ,   "current channel"                                 )
+/*
+ * Actual control value sent to etalon for this position
+ */
+MK_INT( 4305.0, BINVAL  ,   "binary control value"                            )
+
+/*
+ * GriF needed a bunch more values so some of these are duplicates :-(
+ */
+/*
+ * GriF adds a Fabre-Perot etalon between AOB and KIR.  This value indicates
+ * the position of the slide holding the etalon.
+ */
+MK_STR( 4320.0, GRFPPOS ,   "GriF position of FP carriage - In/Out"           )
+/*
+ * Etalon calibration values, order and standard wavelength, and BCV at
+ * standard wavelength
+ */
+MK_FLT( 4321.1, GRWAVORD,6, "GriF wavelength of order used to scan"           )
+MK_FLT( 4321.2, GRWAVCAL,6, "GriF wavelength of calibration spectral line"    )
+MK_FLT( 4321.3, GRBCVCAL,6, "GriF BCV at calibration line"                    )
+/*
+ * This records what the driver for the scan is
+ *   bcv      - specific BCV positions given
+ *   wave     - a wavelength range given and BCV's calculated
+ *   velocity - a range of delta velocities given and BCV's calculated
+ */
+MK_STR( 4322.0, GRSCANTY,   "GriF type of scan - bcv, wave, velocity"         )
+/*
+ * Wavelength values for scan, beginning, delta, and current
+ */
+MK_FLT( 4323.1, GRWAVBEG,6, "GriF wavelength at beginning of scan"            )
+MK_FLT( 4323.2, GRWAVSTP,6, "GriF wavelength step"                            )
+MK_FLT( 4323.3, GRWAVCUR,6, "GriF current observed wavelength"                )
+/*
+ * BCV equivalents (well, these are calculated from the desired wavelengths
+ * initially, but since the etalon is integral, the current wavelength value
+ * above is a conversion back from the BCV position)
+ */
+MK_FLT( 4324.1, GRBCVBEG,6, "GriF Binary Contol Value at beginning of scan"   )
+MK_FLT( 4324.2, GRBCVSTP,6, "GriF BCV step"                                   )
+MK_INT( 4324.3, GRBCVCUR,   "GriF applied BCV"                                )
+/*
+ * The scan itself is a sequence of etalon positions rounded from the
+ * desired wavelength range and step.  These are the count and current
+ * position in the scan.  (How is GRCURRCH different from GRBCVCUR ??? )
+ */
+MK_INT( 4325.1, GRNBCHAN,   "GriF number of channels in scan"                 )
+MK_INT( 4325.2, GRCURRCH,   "GriF current channel"                            )
+/*
+ * GriF also controls the plate parallelism.  These record the x and y
+ * commands
+ */
+MK_INT( 4326.1, GRBCV_X ,   "GriF X Binary Control Value FP"                  )
+MK_INT( 4326.2, GRBCV_Y ,   "GriF Y Binary Control Value FP"                  )
+
+/*
+ * Environment : 9000.000 to 9999.999
+ *
+ *TYPE --IDX--  KEYWORD- PRC -------------------COMMENT----------------------*/
+
+/*
+ * Queue Scheduled Observing : 11000.000 to 11999.999
+ *
+ *TYPE --IDX--  KEYWORD- PRC -------------------COMMENT----------------------*/
+
+/*
+ * Processing Pipeline : 15000.000 to 15999.999
+ *
+ *TYPE --IDX--  KEYWORD- PRC -------------------COMMENT----------------------*/
+
+/*
+ * Archive System : 90000.000 to 99999.999
+ *
+ *TYPE --IDX--  KEYWORD- PRC -------------------COMMENT----------------------*/
+
+#endif /* !_INCLUDED_fh_registry */
Index: /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfh/fh_registry_detcom_notes
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfh/fh_registry_detcom_notes	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfh/fh_registry_detcom_notes	(revision 23594)
@@ -0,0 +1,191 @@
+Block
+0	PHU
+1	PHU
+2	EHU1
+3	EHU1
+4	EHU1
+5	DATA(1)
+6	DATA(2)
+...
+5967	DATA(5963)
+5968	EHU2
+5969	EHU2
+5970	EHU2
+5971	DATA(1)
+...
+11933	DATA(5963)
+11934	EHU3
+11935	EHU3
+11936	EHU3
+11937	DATA(1)
+...
+17900	EHU4
+23866	EHU5
+29832	EHU6
+35798	EHU7
+41764	EHU8
+47730	EHU9
+53696	EHU10
+59662	EHU11
+65628	EHU12
+65629	EHU12
+65630	EHU12
+65631	DATA(1)
+71593	DATA(5963)
+EOF
+
+Index	Header name	Example Value
+
+0	SIMPLE		T
+0	XTENSION	'IMAGE   '
+1	BITPIX		16 / Values from 0..65535 (BZERO=32768)
+2	NAXIS		2
+2.1	NAXIS1		2080
+2.2	NAXIS2		4128
+3	EXTEND		T
+3.1	NEXTEND		12
+4	GROUPS		T
+5	PCOUNT		0
+6	GCOUNT		1
+7	TFIELDS		10
+7.100	TFORM1
+7.101	TBCOL1		
+7.200	TFORM2		
+7.201	TBCOL2	
+100.0	COMMENT
+100.1	COMMENT  General Information
+100.2	COMMENT  -------------------
+101	FILENAME	'12345o  '
+102	AUTHOR
+103	REFERENC	
+104	DATE		'1999-02-05-12:00:53'
+105	ORIGIN		'CFHT    '
+110	IMAGESWV	'12kcom version 1.2'
+111	IMAGEHWV
+	CHECKVER
+	CHECKSUM
+	DATASUM
+100.0	COMMENT                              06 07 08 09 10 11
+100.1	COMMENT  Image Extension for Chip 2  
+100.2	COMMENT  --------------------------  00 01[02]03 04 05
+101.1	EXTNAME		'chip02'
+101.2	EXTVER		2
+101.3	EXTLEVEL	1
+199	INHERIT		F
+200.0	COMMENT
+200.1	COMMENT	Info about Observations
+200.2	COMMENT -----------------------
+201	TIMESYS		'UTC     '
+202	DATE-OBS	'1999-02-05'
+203	UTC-OBS
+204	MJD-OBS
+205	LST-OBS
+210	OBSTYPE
+220	EXPTYPE
+221	EXPTIME
+1000.0	COMMENT
+1000.1	COMMENT  Detector array and CCD Information
+1000.2	COMMENT	 ----------------------------------
+1001	DETECTOR  	'CFH12K'
+1002	DETSIZE   	'[1:12450,1:8230]'
+1003	NCCDS	  	12
+1004	NAMPS		12
+1005	CCDSUM		'2 2'
+1005.1	CCDBIN1		2
+1005.2	CCDBIN2		2
+1006	RASTER
+1010.1	PIXSIZE1
+1010.2	PIXSIZE2
+1011.1	PIXSCAL1
+1011.2	PIXSCAL2
+1101	BZERO		32768
+1102	BSCALE		1
+1103	BUNIT
+1110	BLANK
+1201.n	CTYPEn
+1202.n	CRPIXn
+1202.n	CRVALn
+1203.n	CDELTn
+1204.n	CROTAn
+1205.ioj	CDi_j
+1211.ioj	LIMi_j
+1212.
+
+1301	DATAMAX
+1302	DATAMIN
+
+	OBSERVAT	'CFHT    '
+	TELESCOP	'CFHT 3.6m'
+	TELCONF
+	TELTCS
+	TELFOCUS
+	INSTRUME	'CFH12K Mosaic'
+	OBSERVER
+	OBJECT		
+
+	OBJNAME
+	OBJTYPE
+	OBJRA
+	OBJDEC
+
+	EQUINOX
+	EPOCH		(depr.)
+
+	RADECSYS	'FK5     '
+	RA
+	DEC
+	
+
+	OBSID
+	IMAGEID
+
+	OBSTYPE
+	EXPTIME
+	DARKTIME
+	NSUBEXPS
+	EXPREQ
+	OBSSTAT
+	OBSEQUIN
+	OBSRA
+	OBSDEC
+	
+	AIRMASS
+	AIRMASSn
+	AMMJDn
+	
+	FOCNEXPO
+
+	FOCSTART
+	FOCSTEP
+	FOCSHIFT
+	
+	LAMP
+	LAMPTYPE
+
+NOAO's major groups
+
+Image	BSCALE, NEXTEND, TIMESYS, DATE
+Site	OBSERVAT SEEING WEATHER ENVIRONMENT
+Dome	WIND TEMP
+Telescope TELESCOP TELCONF CHOP AO, etc.
+TV
+Guider
+Adapter
+Observation OBSID, OBJECT FOCNEXPO LAMP
+Object	OBJxxx
+Observer	OBSERVER
+Instrument INSTRUME INSTCONF INSFOCUS
+Detector DETECTOR DETSIZE NCCDS NAMPS PIXSCALi DISPAXIS DISPWC 
+           CCDNAME PIXSIZEi CCDSIZE CCDPSIZE CCDNAMPS AMPSIZE CCDSEC CCDSUM BAISSEC TRIMSEC DATASEC AMPSEC DETSEC
+		CTYPE CRVALi CRPIXi CDi_j 
+Aperture
+Disperser
+Filter	FILTERn FILPOSn
+Camera	CAMERA CAMCONF CAMFOCUS
+Shutter	SHUTSTAT SHUTOPEN SHUTCLOS
+Processing	PROCSTAT PIPELINE
+Archive		ARCHIVE
+
+
+
+	
Index: /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfh/fh_registry_to_asm.sh
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfh/fh_registry_to_asm.sh	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfh/fh_registry_to_asm.sh	(revision 23594)
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+cat - | \
+egrep -e '^MK_(FLT|INT|BLN|STR|VAL)' | \
+sed -e 's/^MK_....[^!-~]*\([0-9][0-9.]*\)[^!-~]*,[^!-~]*\([A-Z0-9_][A-Z0-9_]*\)[^"]*"\(.*\)"[^"]*$/DETCOM_\2 MACRO ARG\
+    COBJ "detcom:dheader \1 \2 \\"\\ARG\\" \\"\3\\""\
+    ENDM/'
Index: /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfh/fh_rt.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfh/fh_rt.c	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfh/fh_rt.c	(revision 23594)
@@ -0,0 +1,47 @@
+/*                                             -*- c-file-style: "Ellemtel" -*-
+
+`fh_rt.c' - FITS RealTime routines
+
+This file is part of version 1 of the FITS Handling Library.
+Read the `License' file for terms of use and distribution.
+Copyright 2001, Canada-France-Hawaii Telescope, daprog@cfht.hawaii.edu.
+
+___This header automatically generated from `Index'. Do not edit it here!___ */
+
+#include "fh.h"
+#include "fh_rt.h"
+
+fh_bool
+fh_rt_present(HeaderUnit hu)
+{
+}
+
+fh_bool
+fh_rt_active(HeaderUnit hu)
+{
+   if (fh_rt_present(hu) == FH_FALSE) then return FH_FALSE;
+}
+
+int
+fh_rt_version(HeaderUnit hu)
+{
+   if (fh_rt_active(hu) == FH_FALSE)
+      return ...;
+}
+
+int
+fh_rt_plane_completed(HeaderUnit hu)
+{
+   if (fh_rt_active(hu) == FH_FALSE)
+
+}
+
+int
+fh_rt_plane_latest(HeaderUnit hu)
+{
+}
+
+int
+fh_rt_plane_row_latest(HeaderUnit hu, int plane)
+{
+}
Index: /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfh/fh_rt.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfh/fh_rt.h	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfh/fh_rt.h	(revision 23594)
@@ -0,0 +1,20 @@
+/*                                             -*- c-file-style: "Ellemtel" -*-
+
+`fh_rt.h' - FITS RealTime routines
+
+This file is part of version 1 of the FITS Handling Library.
+Read the `License' file for terms of use and distribution.
+Copyright 2001, Canada-France-Hawaii Telescope, daprog@cfht.hawaii.edu.
+
+___This header automatically generated from `Index'. Do not edit it here!___ */
+#ifndef _INCLUDED_fh_rt
+#define _INCLUDED_fh_rt 1
+
+fh_bool fh_rt_present(HeaderUnit hu);
+fh_bool fh_rt_active(HeaderUnit hu);
+int     fh_rt_version(HeaderUnit hu);
+int     fh_rt_plane_completed(HeaderUnit hu); /* Returns 0..(NAXIS3-1) */
+int     fh_rt_plane_latest(HeaderUnit hu);    /* Returns 0..(NAXIS3-1) */
+int     fh_rt_plane_row_latest(HeaderUnit hu, int plane); /* 0..(NAXIS2-1) */
+
+#endif
Index: /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfh/fh_validate.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfh/fh_validate.c	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfh/fh_validate.c	(revision 23594)
@@ -0,0 +1,233 @@
+/*                                             -*- c-file-style: "Ellemtel" -*-
+
+`fh_validate.c' - A routine to validate the completeness of header.
+
+This file is part of version 1 of the FITS Handling Library.
+Read the `License' file for terms of use and distribution.
+Copyright 2001, Canada-France-Hawaii Telescope, daprog@cfht.hawaii.edu.
+
+___This header automatically generated from `Index'. Do not edit it here!___ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "fh.h"
+
+fh_result
+fh_validate(HeaderUnit hu)
+{
+   HeaderUnitStruct* list = FH_HU(hu);
+   fh_result rtn = FH_SUCCESS;
+   int i, xtension=0, naxis=0, bitpix=0;
+   int warned_comment = 0;
+   char* s;
+
+   fh_sort(list);
+
+   for (i=0; i<list->len; i++)
+   {
+      s = list->hdr[i]->card;
+
+      if (fix_characters(s, /*repair=*/1) != FH_SUCCESS)
+      {
+	 if (rtn == FH_SUCCESS) rtn = FH_BAD_VALUE;
+	 fprintf(stderr, "error: libfh: Illegal character(s) in FITS card [%.8s]\n", s);
+      }
+      else
+      {
+	 if (!memcmp(s, "COMMENT ", 8) ||
+	     !memcmp(s, "HISTORY ", 8) ||
+	     !memcmp(s, "        ", 8))
+	 {
+	    if (s[8] == '=')
+	    {
+	       rtn = FH_BAD_VALUE;
+	       if (!warned_comment++)
+		  fprintf(stderr,
+			  "warning: libfh: FITS standard recommends COMMENT/HISTORY not begin with '='\n");
+	    }
+	 }
+	 else
+	 {
+	    if (s[8] == '=')
+	    {
+	       int j;
+	       fh_bool k;
+	       char tmp[80];
+
+	       if (s[9] != ' ')
+	       {
+		  rtn = FH_INVALID;
+		  fprintf(stderr,
+			  "error: libfh: Missing ' ' character after [%.9s]; Not correcting.\n", s);
+	       }
+	       /*
+		* Search for duplicated cards
+		*/
+	       for (j = i-1; j>=0; j--)
+	       {
+		  if (!memcmp(s, list->hdr[j]->card, 8))
+		  {
+		     rtn = FH_INVALID;
+		     fprintf(stderr,
+			     "error: libfh: Line %d:[%.80s] duplicates previous line %d:[%.80s]\n",
+			     i, s, j, list->hdr[j]->card);
+		  }
+	       }
+	       for (j = 10; j < 80; j++)
+		  if (s[j] != ' ') break;
+
+	       if (j < 80) switch (s[j])
+	       {
+		  case '\'': /* Check for fixed format string */
+		  {
+		     if (j != 10 || fh_get_str(list, s, tmp, sizeof(tmp)) != FH_SUCCESS)
+		     {
+			rtn = FH_BAD_VALUE;
+			fprintf(stderr, "warning: libfh: String [%.80s] is not in \"fixed\" format.\n", s);
+		     }
+		     break;
+		  }
+		  case 'T': /* Check for fixed format boolean */
+		  case 'F':
+		  {
+		     if (j != 29 || fh_get_bool(list, s, &k) != FH_SUCCESS)
+		     {
+			rtn = FH_BAD_VALUE;
+			fprintf(stderr, "error: libfh: Boolean [%.8s] is not in \"fixed\" format.\n", s);
+		     }
+		     for (j = 31; j < 80; j++)
+			if (s[j] != ' ') break;
+		     if (j < 80 && s[j] != '/')
+		     {
+			rtn = FH_BAD_VALUE;
+			fprintf(stderr, "error: libfh: Garbage after boolean [%.8s] value field.\n", s);
+		     }
+		     break;
+		  }
+		  case '/': /* No value, only comment field */
+		  {
+		     if (j < 31)
+		     {
+			rtn = FH_BAD_VALUE;
+			fprintf(stderr, "error: libfh: Comment field for [%.8s] begins before column 32\n", s);
+		     }
+		     break;
+		  }
+		  case '(': /* Complex value.  No checking (no fixed format either.) */
+		     break;
+		  default:
+		  {
+		     for (k=j; k < 80; k++) /* Find the end of the value. */
+			if (s[k] == ' ') break;
+		     if ((j > 10 && k != 30) || (k < 30))
+		     {
+			rtn = FH_BAD_VALUE;
+			fprintf(stderr, "error: libfh: Value of [%.8s] is not in \"fixed\" format.\n", s);
+		     }
+		  }
+	       } /* switch (s[j]) */
+	    }
+	    else
+	    {
+	       /*
+		* The '=' sign is missing, so this should be treated as a comment
+		* line.  However, since we do not use any other comment cards
+		* in our files other than COMMENT HISTORY or "        ", this is
+		* probably an error.
+		*/
+	       rtn = FH_INVALID;
+	       fprintf(stderr,
+		       "warning: libfh: Treating [%.8s] as a comment card (No '=' in column 9)\n", s);
+	    }
+	 }
+
+	 switch (i)
+	 {
+	    case 0: /* must be SIMPLE T or XTENSION */
+	    {
+	       if (!memcmp(s, "XTENSION", 8))
+		  xtension=1;
+	       else
+	       {
+		  if (memcmp(s, "SIMPLE  =                    T ", 31))
+		  {
+		     rtn = FH_INVALID;
+		     fprintf(stderr, "error: libfh: First card is not \"SIMPLE  = ... T\"\n");
+		  }
+	       }
+	       break;
+	    }
+            case 1: /* must be BITPIX */
+	    {
+	       if (memcmp(s, "BITPIX  ", 8) || fh_get_int(list, "BITPIX", &bitpix) != FH_SUCCESS)
+	       {
+		  rtn = FH_INVALID;
+		  fprintf(stderr, "error: libfh: Second card is not \"BITPIX  = \"\n");
+		  bitpix = 0;
+	       }
+	       else switch (bitpix)
+	       {
+		  case 8:
+		  case 16:
+		  case 32:
+		  case -32:
+		  case -64: break; /* Valid */
+		  case 0: if (xtension) break;
+		  default:
+		  {
+		     rtn = FH_INVALID;
+		     fprintf(stderr, "error: libfh: Non-standard BITPIX: %d\n", bitpix);
+		  }
+	       }
+	       break;
+	    }
+	    case 2: /* must be NAXIS */
+	    {
+	       if (memcmp(s, "NAXIS   ", 8) || fh_get_int(list, "NAXIS", &naxis) != FH_SUCCESS)
+	       {
+		  rtn = FH_INVALID;
+		  fprintf(stderr, "error: libfh: Third card is not \"NAXIS   = \"\n");
+	       }
+	       break;
+	    }
+            default: /* NAXIS??? must be next, then PCOUNT, GCOUNT if XTENSION */
+	    {
+	       if (i <= naxis + 2)
+	       {
+		  char name[9] = "        ";
+		  
+		  sprintf(name, "NAXIS%d", i - 2);
+		  if (fh_cmp(s, name) < MATCH_KEYWORD)
+		  {
+		     rtn = FH_INVALID;
+		     fprintf(stderr, "error: libfh: Found [%.8s] instead of [%s].\n",
+			     s, name);
+		  }
+	       }
+	       else if (xtension && i == naxis + 2 + 1)
+	       {
+		  if (memcmp(s, "PCOUNT  ", 8))
+		  {
+		     rtn = FH_INVALID;
+		     fprintf(stderr,
+			     "error: libfh: Extension has [%.8s] where PCOUNT should be.\n",
+			     s);
+		  }
+	       }
+	       else if (xtension && i == naxis + 2 + 2)
+	       {
+		  if (memcmp(s, "GCOUNT  ", 8))
+		  {
+		     rtn = FH_INVALID;
+		     fprintf(stderr, "error: libfh: Extension has [%.8s] where GCOUNT should be.\n",
+			     s);
+		  }
+	       }
+	    }
+	 }
+      }
+   }
+
+   return rtn;
+}
Index: /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfh/tests/t.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfh/tests/t.c	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfh/tests/t.c	(revision 23594)
@@ -0,0 +1,2 @@
+#include "../fh.h"
+#include "../fh_registry.h"
Index: /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfh/tests/t.log
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfh/tests/t.log	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfh/tests/t.log	(revision 23594)
@@ -0,0 +1,2345 @@
+../fh_registry.h:203: warning: `fh_set_SIMPLE' defined but not used
+../fh_registry.h:203: warning: `fh_setval_SIMPLE' defined but not used
+../fh_registry.h:203: warning: `fh_setcmt_SIMPLE' defined but not used
+../fh_registry.h:203: warning: `fh_get_SIMPLE' defined but not used
+../fh_registry.h:203: warning: `fh_kw_SIMPLE' defined but not used
+../fh_registry.h:203: warning: `fh_cmt_SIMPLE' defined but not used
+../fh_registry.h:204: warning: `fh_set_XTENSION' defined but not used
+../fh_registry.h:204: warning: `fh_setval_XTENSION' defined but not used
+../fh_registry.h:204: warning: `fh_setcmt_XTENSION' defined but not used
+../fh_registry.h:204: warning: `fh_get_XTENSION' defined but not used
+../fh_registry.h:204: warning: `fh_kw_XTENSION' defined but not used
+../fh_registry.h:204: warning: `fh_cmt_XTENSION' defined but not used
+../fh_registry.h:205: warning: `fh_set_BITPIX' defined but not used
+../fh_registry.h:205: warning: `fh_setval_BITPIX' defined but not used
+../fh_registry.h:205: warning: `fh_setcmt_BITPIX' defined but not used
+../fh_registry.h:205: warning: `fh_get_BITPIX' defined but not used
+../fh_registry.h:205: warning: `fh_kw_BITPIX' defined but not used
+../fh_registry.h:205: warning: `fh_cmt_BITPIX' defined but not used
+../fh_registry.h:206: warning: `fh_set_NAXIS' defined but not used
+../fh_registry.h:206: warning: `fh_setval_NAXIS' defined but not used
+../fh_registry.h:206: warning: `fh_setcmt_NAXIS' defined but not used
+../fh_registry.h:206: warning: `fh_get_NAXIS' defined but not used
+../fh_registry.h:206: warning: `fh_kw_NAXIS' defined but not used
+../fh_registry.h:206: warning: `fh_cmt_NAXIS' defined but not used
+../fh_registry.h:207: warning: `fh_set_NAXIS1' defined but not used
+../fh_registry.h:207: warning: `fh_setval_NAXIS1' defined but not used
+../fh_registry.h:207: warning: `fh_setcmt_NAXIS1' defined but not used
+../fh_registry.h:207: warning: `fh_get_NAXIS1' defined but not used
+../fh_registry.h:207: warning: `fh_kw_NAXIS1' defined but not used
+../fh_registry.h:207: warning: `fh_cmt_NAXIS1' defined but not used
+../fh_registry.h:208: warning: `fh_set_NAXIS2' defined but not used
+../fh_registry.h:208: warning: `fh_setval_NAXIS2' defined but not used
+../fh_registry.h:208: warning: `fh_setcmt_NAXIS2' defined but not used
+../fh_registry.h:208: warning: `fh_get_NAXIS2' defined but not used
+../fh_registry.h:208: warning: `fh_kw_NAXIS2' defined but not used
+../fh_registry.h:208: warning: `fh_cmt_NAXIS2' defined but not used
+../fh_registry.h:209: warning: `fh_set_NAXIS3' defined but not used
+../fh_registry.h:209: warning: `fh_setval_NAXIS3' defined but not used
+../fh_registry.h:209: warning: `fh_setcmt_NAXIS3' defined but not used
+../fh_registry.h:209: warning: `fh_get_NAXIS3' defined but not used
+../fh_registry.h:209: warning: `fh_kw_NAXIS3' defined but not used
+../fh_registry.h:209: warning: `fh_cmt_NAXIS3' defined but not used
+../fh_registry.h:210: warning: `fh_set_EXTEND' defined but not used
+../fh_registry.h:210: warning: `fh_setval_EXTEND' defined but not used
+../fh_registry.h:210: warning: `fh_setcmt_EXTEND' defined but not used
+../fh_registry.h:210: warning: `fh_get_EXTEND' defined but not used
+../fh_registry.h:210: warning: `fh_kw_EXTEND' defined but not used
+../fh_registry.h:210: warning: `fh_cmt_EXTEND' defined but not used
+../fh_registry.h:211: warning: `fh_set_NEXTEND' defined but not used
+../fh_registry.h:211: warning: `fh_setval_NEXTEND' defined but not used
+../fh_registry.h:211: warning: `fh_setcmt_NEXTEND' defined but not used
+../fh_registry.h:211: warning: `fh_get_NEXTEND' defined but not used
+../fh_registry.h:211: warning: `fh_kw_NEXTEND' defined but not used
+../fh_registry.h:211: warning: `fh_cmt_NEXTEND' defined but not used
+../fh_registry.h:212: warning: `fh_set_GROUPS' defined but not used
+../fh_registry.h:212: warning: `fh_setval_GROUPS' defined but not used
+../fh_registry.h:212: warning: `fh_setcmt_GROUPS' defined but not used
+../fh_registry.h:212: warning: `fh_get_GROUPS' defined but not used
+../fh_registry.h:212: warning: `fh_kw_GROUPS' defined but not used
+../fh_registry.h:212: warning: `fh_cmt_GROUPS' defined but not used
+../fh_registry.h:213: warning: `fh_set_PCOUNT' defined but not used
+../fh_registry.h:213: warning: `fh_setval_PCOUNT' defined but not used
+../fh_registry.h:213: warning: `fh_setcmt_PCOUNT' defined but not used
+../fh_registry.h:213: warning: `fh_get_PCOUNT' defined but not used
+../fh_registry.h:213: warning: `fh_kw_PCOUNT' defined but not used
+../fh_registry.h:213: warning: `fh_cmt_PCOUNT' defined but not used
+../fh_registry.h:214: warning: `fh_set_GCOUNT' defined but not used
+../fh_registry.h:214: warning: `fh_setval_GCOUNT' defined but not used
+../fh_registry.h:214: warning: `fh_setcmt_GCOUNT' defined but not used
+../fh_registry.h:214: warning: `fh_get_GCOUNT' defined but not used
+../fh_registry.h:214: warning: `fh_kw_GCOUNT' defined but not used
+../fh_registry.h:214: warning: `fh_cmt_GCOUNT' defined but not used
+../fh_registry.h:215: warning: `fh_set_TFIELDS' defined but not used
+../fh_registry.h:215: warning: `fh_setval_TFIELDS' defined but not used
+../fh_registry.h:215: warning: `fh_setcmt_TFIELDS' defined but not used
+../fh_registry.h:215: warning: `fh_get_TFIELDS' defined but not used
+../fh_registry.h:215: warning: `fh_kw_TFIELDS' defined but not used
+../fh_registry.h:215: warning: `fh_cmt_TFIELDS' defined but not used
+../fh_registry.h:216: warning: `fh_set_TFORM1' defined but not used
+../fh_registry.h:216: warning: `fh_setval_TFORM1' defined but not used
+../fh_registry.h:216: warning: `fh_setcmt_TFORM1' defined but not used
+../fh_registry.h:216: warning: `fh_get_TFORM1' defined but not used
+../fh_registry.h:216: warning: `fh_kw_TFORM1' defined but not used
+../fh_registry.h:216: warning: `fh_cmt_TFORM1' defined but not used
+../fh_registry.h:217: warning: `fh_set_TBCOL1' defined but not used
+../fh_registry.h:217: warning: `fh_setval_TBCOL1' defined but not used
+../fh_registry.h:217: warning: `fh_setcmt_TBCOL1' defined but not used
+../fh_registry.h:217: warning: `fh_get_TBCOL1' defined but not used
+../fh_registry.h:217: warning: `fh_kw_TBCOL1' defined but not used
+../fh_registry.h:217: warning: `fh_cmt_TBCOL1' defined but not used
+../fh_registry.h:218: warning: `fh_set_TFORM2' defined but not used
+../fh_registry.h:218: warning: `fh_setval_TFORM2' defined but not used
+../fh_registry.h:218: warning: `fh_setcmt_TFORM2' defined but not used
+../fh_registry.h:218: warning: `fh_get_TFORM2' defined but not used
+../fh_registry.h:218: warning: `fh_kw_TFORM2' defined but not used
+../fh_registry.h:218: warning: `fh_cmt_TFORM2' defined but not used
+../fh_registry.h:219: warning: `fh_set_TBCOL2' defined but not used
+../fh_registry.h:219: warning: `fh_setval_TBCOL2' defined but not used
+../fh_registry.h:219: warning: `fh_setcmt_TBCOL2' defined but not used
+../fh_registry.h:219: warning: `fh_get_TBCOL2' defined but not used
+../fh_registry.h:219: warning: `fh_kw_TBCOL2' defined but not used
+../fh_registry.h:219: warning: `fh_cmt_TBCOL2' defined but not used
+../fh_registry.h:220: warning: `fh_set_TFORM3' defined but not used
+../fh_registry.h:220: warning: `fh_setval_TFORM3' defined but not used
+../fh_registry.h:220: warning: `fh_setcmt_TFORM3' defined but not used
+../fh_registry.h:220: warning: `fh_get_TFORM3' defined but not used
+../fh_registry.h:220: warning: `fh_kw_TFORM3' defined but not used
+../fh_registry.h:220: warning: `fh_cmt_TFORM3' defined but not used
+../fh_registry.h:221: warning: `fh_set_TBCOL3' defined but not used
+../fh_registry.h:221: warning: `fh_setval_TBCOL3' defined but not used
+../fh_registry.h:221: warning: `fh_setcmt_TBCOL3' defined but not used
+../fh_registry.h:221: warning: `fh_get_TBCOL3' defined but not used
+../fh_registry.h:221: warning: `fh_kw_TBCOL3' defined but not used
+../fh_registry.h:221: warning: `fh_cmt_TBCOL3' defined but not used
+../fh_registry.h:222: warning: `fh_set_TFORM4' defined but not used
+../fh_registry.h:222: warning: `fh_setval_TFORM4' defined but not used
+../fh_registry.h:222: warning: `fh_setcmt_TFORM4' defined but not used
+../fh_registry.h:222: warning: `fh_get_TFORM4' defined but not used
+../fh_registry.h:222: warning: `fh_kw_TFORM4' defined but not used
+../fh_registry.h:222: warning: `fh_cmt_TFORM4' defined but not used
+../fh_registry.h:223: warning: `fh_set_TBCOL4' defined but not used
+../fh_registry.h:223: warning: `fh_setval_TBCOL4' defined but not used
+../fh_registry.h:223: warning: `fh_setcmt_TBCOL4' defined but not used
+../fh_registry.h:223: warning: `fh_get_TBCOL4' defined but not used
+../fh_registry.h:223: warning: `fh_kw_TBCOL4' defined but not used
+../fh_registry.h:223: warning: `fh_cmt_TBCOL4' defined but not used
+../fh_registry.h:224: warning: `fh_set_TFORM5' defined but not used
+../fh_registry.h:224: warning: `fh_setval_TFORM5' defined but not used
+../fh_registry.h:224: warning: `fh_setcmt_TFORM5' defined but not used
+../fh_registry.h:224: warning: `fh_get_TFORM5' defined but not used
+../fh_registry.h:224: warning: `fh_kw_TFORM5' defined but not used
+../fh_registry.h:224: warning: `fh_cmt_TFORM5' defined but not used
+../fh_registry.h:225: warning: `fh_set_TBCOL5' defined but not used
+../fh_registry.h:225: warning: `fh_setval_TBCOL5' defined but not used
+../fh_registry.h:225: warning: `fh_setcmt_TBCOL5' defined but not used
+../fh_registry.h:225: warning: `fh_get_TBCOL5' defined but not used
+../fh_registry.h:225: warning: `fh_kw_TBCOL5' defined but not used
+../fh_registry.h:225: warning: `fh_cmt_TBCOL5' defined but not used
+../fh_registry.h:226: warning: `fh_set_BZERO' defined but not used
+../fh_registry.h:226: warning: `fh_setval_BZERO' defined but not used
+../fh_registry.h:226: warning: `fh_setcmt_BZERO' defined but not used
+../fh_registry.h:226: warning: `fh_get_BZERO' defined but not used
+../fh_registry.h:226: warning: `fh_kw_BZERO' defined but not used
+../fh_registry.h:226: warning: `fh_cmt_BZERO' defined but not used
+../fh_registry.h:227: warning: `fh_set_BSCALE' defined but not used
+../fh_registry.h:227: warning: `fh_setval_BSCALE' defined but not used
+../fh_registry.h:227: warning: `fh_setcmt_BSCALE' defined but not used
+../fh_registry.h:227: warning: `fh_get_BSCALE' defined but not used
+../fh_registry.h:227: warning: `fh_kw_BSCALE' defined but not used
+../fh_registry.h:227: warning: `fh_cmt_BSCALE' defined but not used
+../fh_registry.h:233: warning: `fh_set_CMTSUM1' defined but not used
+../fh_registry.h:234: warning: `fh_set_CMTSUM2' defined but not used
+../fh_registry.h:235: warning: `fh_set_CMTSUM3' defined but not used
+../fh_registry.h:236: warning: `fh_set_CMTSUM4' defined but not used
+../fh_registry.h:237: warning: `fh_set_CMTSUM5' defined but not used
+../fh_registry.h:238: warning: `fh_set_CMMTOBS' defined but not used
+../fh_registry.h:238: warning: `fh_setval_CMMTOBS' defined but not used
+../fh_registry.h:238: warning: `fh_setcmt_CMMTOBS' defined but not used
+../fh_registry.h:238: warning: `fh_get_CMMTOBS' defined but not used
+../fh_registry.h:238: warning: `fh_kw_CMMTOBS' defined but not used
+../fh_registry.h:238: warning: `fh_cmt_CMMTOBS' defined but not used
+../fh_registry.h:239: warning: `fh_set_CMMTSEQ' defined but not used
+../fh_registry.h:239: warning: `fh_setval_CMMTSEQ' defined but not used
+../fh_registry.h:239: warning: `fh_setcmt_CMMTSEQ' defined but not used
+../fh_registry.h:239: warning: `fh_get_CMMTSEQ' defined but not used
+../fh_registry.h:239: warning: `fh_kw_CMMTSEQ' defined but not used
+../fh_registry.h:239: warning: `fh_cmt_CMMTSEQ' defined but not used
+../fh_registry.h:240: warning: `fh_set_OBJECT' defined but not used
+../fh_registry.h:240: warning: `fh_setval_OBJECT' defined but not used
+../fh_registry.h:240: warning: `fh_setcmt_OBJECT' defined but not used
+../fh_registry.h:240: warning: `fh_get_OBJECT' defined but not used
+../fh_registry.h:240: warning: `fh_kw_OBJECT' defined but not used
+../fh_registry.h:240: warning: `fh_cmt_OBJECT' defined but not used
+../fh_registry.h:241: warning: `fh_set_OBSERVER' defined but not used
+../fh_registry.h:241: warning: `fh_setval_OBSERVER' defined but not used
+../fh_registry.h:241: warning: `fh_setcmt_OBSERVER' defined but not used
+../fh_registry.h:241: warning: `fh_get_OBSERVER' defined but not used
+../fh_registry.h:241: warning: `fh_kw_OBSERVER' defined but not used
+../fh_registry.h:241: warning: `fh_cmt_OBSERVER' defined but not used
+../fh_registry.h:242: warning: `fh_set_PI_NAME' defined but not used
+../fh_registry.h:242: warning: `fh_setval_PI_NAME' defined but not used
+../fh_registry.h:242: warning: `fh_setcmt_PI_NAME' defined but not used
+../fh_registry.h:242: warning: `fh_get_PI_NAME' defined but not used
+../fh_registry.h:242: warning: `fh_kw_PI_NAME' defined but not used
+../fh_registry.h:242: warning: `fh_cmt_PI_NAME' defined but not used
+../fh_registry.h:243: warning: `fh_set_RUNID' defined but not used
+../fh_registry.h:243: warning: `fh_setval_RUNID' defined but not used
+../fh_registry.h:243: warning: `fh_setcmt_RUNID' defined but not used
+../fh_registry.h:243: warning: `fh_get_RUNID' defined but not used
+../fh_registry.h:243: warning: `fh_kw_RUNID' defined but not used
+../fh_registry.h:243: warning: `fh_cmt_RUNID' defined but not used
+../fh_registry.h:249: warning: `fh_set_CMTGEN1' defined but not used
+../fh_registry.h:250: warning: `fh_set_CMTGEN2' defined but not used
+../fh_registry.h:251: warning: `fh_set_CMTGEN3' defined but not used
+../fh_registry.h:252: warning: `fh_set_CMTGEN4' defined but not used
+../fh_registry.h:253: warning: `fh_set_FILENAME' defined but not used
+../fh_registry.h:253: warning: `fh_setval_FILENAME' defined but not used
+../fh_registry.h:253: warning: `fh_setcmt_FILENAME' defined but not used
+../fh_registry.h:253: warning: `fh_get_FILENAME' defined but not used
+../fh_registry.h:253: warning: `fh_kw_FILENAME' defined but not used
+../fh_registry.h:253: warning: `fh_cmt_FILENAME' defined but not used
+../fh_registry.h:254: warning: `fh_set_PATHNAME' defined but not used
+../fh_registry.h:254: warning: `fh_setval_PATHNAME' defined but not used
+../fh_registry.h:254: warning: `fh_setcmt_PATHNAME' defined but not used
+../fh_registry.h:254: warning: `fh_get_PATHNAME' defined but not used
+../fh_registry.h:254: warning: `fh_kw_PATHNAME' defined but not used
+../fh_registry.h:254: warning: `fh_cmt_PATHNAME' defined but not used
+../fh_registry.h:255: warning: `fh_set_EXTNAME' defined but not used
+../fh_registry.h:255: warning: `fh_setval_EXTNAME' defined but not used
+../fh_registry.h:255: warning: `fh_setcmt_EXTNAME' defined but not used
+../fh_registry.h:255: warning: `fh_get_EXTNAME' defined but not used
+../fh_registry.h:255: warning: `fh_kw_EXTNAME' defined but not used
+../fh_registry.h:255: warning: `fh_cmt_EXTNAME' defined but not used
+../fh_registry.h:256: warning: `fh_set_EXTVER' defined but not used
+../fh_registry.h:256: warning: `fh_setval_EXTVER' defined but not used
+../fh_registry.h:256: warning: `fh_setcmt_EXTVER' defined but not used
+../fh_registry.h:256: warning: `fh_get_EXTVER' defined but not used
+../fh_registry.h:256: warning: `fh_kw_EXTVER' defined but not used
+../fh_registry.h:256: warning: `fh_cmt_EXTVER' defined but not used
+../fh_registry.h:257: warning: `fh_set_DATE' defined but not used
+../fh_registry.h:257: warning: `fh_setval_DATE' defined but not used
+../fh_registry.h:257: warning: `fh_setcmt_DATE' defined but not used
+../fh_registry.h:257: warning: `fh_get_DATE' defined but not used
+../fh_registry.h:257: warning: `fh_kw_DATE' defined but not used
+../fh_registry.h:257: warning: `fh_cmt_DATE' defined but not used
+../fh_registry.h:258: warning: `fh_set_HSTTIME' defined but not used
+../fh_registry.h:258: warning: `fh_setval_HSTTIME' defined but not used
+../fh_registry.h:258: warning: `fh_setcmt_HSTTIME' defined but not used
+../fh_registry.h:258: warning: `fh_get_HSTTIME' defined but not used
+../fh_registry.h:258: warning: `fh_kw_HSTTIME' defined but not used
+../fh_registry.h:258: warning: `fh_cmt_HSTTIME' defined but not used
+../fh_registry.h:259: warning: `fh_set_IMAGESWV' defined but not used
+../fh_registry.h:259: warning: `fh_setval_IMAGESWV' defined but not used
+../fh_registry.h:259: warning: `fh_setcmt_IMAGESWV' defined but not used
+../fh_registry.h:259: warning: `fh_get_IMAGESWV' defined but not used
+../fh_registry.h:259: warning: `fh_kw_IMAGESWV' defined but not used
+../fh_registry.h:259: warning: `fh_cmt_IMAGESWV' defined but not used
+../fh_registry.h:264: warning: `fh_set_DETECTOR' defined but not used
+../fh_registry.h:264: warning: `fh_setval_DETECTOR' defined but not used
+../fh_registry.h:264: warning: `fh_setcmt_DETECTOR' defined but not used
+../fh_registry.h:264: warning: `fh_get_DETECTOR' defined but not used
+../fh_registry.h:264: warning: `fh_kw_DETECTOR' defined but not used
+../fh_registry.h:264: warning: `fh_cmt_DETECTOR' defined but not used
+../fh_registry.h:265: warning: `fh_set_INSTRUME' defined but not used
+../fh_registry.h:265: warning: `fh_setval_INSTRUME' defined but not used
+../fh_registry.h:265: warning: `fh_setcmt_INSTRUME' defined but not used
+../fh_registry.h:265: warning: `fh_get_INSTRUME' defined but not used
+../fh_registry.h:265: warning: `fh_kw_INSTRUME' defined but not used
+../fh_registry.h:265: warning: `fh_cmt_INSTRUME' defined but not used
+../fh_registry.h:266: warning: `fh_set_INSTMODE' defined but not used
+../fh_registry.h:266: warning: `fh_setval_INSTMODE' defined but not used
+../fh_registry.h:266: warning: `fh_setcmt_INSTMODE' defined but not used
+../fh_registry.h:266: warning: `fh_get_INSTMODE' defined but not used
+../fh_registry.h:266: warning: `fh_kw_INSTMODE' defined but not used
+../fh_registry.h:266: warning: `fh_cmt_INSTMODE' defined but not used
+../fh_registry.h:272: warning: `fh_set_CMTDET1' defined but not used
+../fh_registry.h:273: warning: `fh_set_CMTDET2' defined but not used
+../fh_registry.h:274: warning: `fh_set_CMTDET3' defined but not used
+../fh_registry.h:275: warning: `fh_set_CMTDET4' defined but not used
+../fh_registry.h:276: warning: `fh_set_DETSIZE' defined but not used
+../fh_registry.h:276: warning: `fh_setval_DETSIZE' defined but not used
+../fh_registry.h:276: warning: `fh_setcmt_DETSIZE' defined but not used
+../fh_registry.h:276: warning: `fh_get_DETSIZE' defined but not used
+../fh_registry.h:276: warning: `fh_kw_DETSIZE' defined but not used
+../fh_registry.h:276: warning: `fh_cmt_DETSIZE' defined but not used
+../fh_registry.h:277: warning: `fh_set_RASTER' defined but not used
+../fh_registry.h:277: warning: `fh_setval_RASTER' defined but not used
+../fh_registry.h:277: warning: `fh_setcmt_RASTER' defined but not used
+../fh_registry.h:277: warning: `fh_get_RASTER' defined but not used
+../fh_registry.h:277: warning: `fh_kw_RASTER' defined but not used
+../fh_registry.h:277: warning: `fh_cmt_RASTER' defined but not used
+../fh_registry.h:278: warning: `fh_set_CCDSUM' defined but not used
+../fh_registry.h:278: warning: `fh_setval_CCDSUM' defined but not used
+../fh_registry.h:278: warning: `fh_setcmt_CCDSUM' defined but not used
+../fh_registry.h:278: warning: `fh_get_CCDSUM' defined but not used
+../fh_registry.h:278: warning: `fh_kw_CCDSUM' defined but not used
+../fh_registry.h:278: warning: `fh_cmt_CCDSUM' defined but not used
+../fh_registry.h:279: warning: `fh_set_CCDBIN1' defined but not used
+../fh_registry.h:279: warning: `fh_setval_CCDBIN1' defined but not used
+../fh_registry.h:279: warning: `fh_setcmt_CCDBIN1' defined but not used
+../fh_registry.h:279: warning: `fh_get_CCDBIN1' defined but not used
+../fh_registry.h:279: warning: `fh_kw_CCDBIN1' defined but not used
+../fh_registry.h:279: warning: `fh_cmt_CCDBIN1' defined but not used
+../fh_registry.h:280: warning: `fh_set_CCDBIN2' defined but not used
+../fh_registry.h:280: warning: `fh_setval_CCDBIN2' defined but not used
+../fh_registry.h:280: warning: `fh_setcmt_CCDBIN2' defined but not used
+../fh_registry.h:280: warning: `fh_get_CCDBIN2' defined but not used
+../fh_registry.h:280: warning: `fh_kw_CCDBIN2' defined but not used
+../fh_registry.h:280: warning: `fh_cmt_CCDBIN2' defined but not used
+../fh_registry.h:281: warning: `fh_set_PIXSIZE' defined but not used
+../fh_registry.h:281: warning: `fh_setval_PIXSIZE' defined but not used
+../fh_registry.h:281: warning: `fh_setcmt_PIXSIZE' defined but not used
+../fh_registry.h:281: warning: `fh_get_PIXSIZE' defined but not used
+../fh_registry.h:281: warning: `fh_kw_PIXSIZE' defined but not used
+../fh_registry.h:281: warning: `fh_cmt_PIXSIZE' defined but not used
+../fh_registry.h:282: warning: `fh_set_PIXSIZE1' defined but not used
+../fh_registry.h:282: warning: `fh_setval_PIXSIZE1' defined but not used
+../fh_registry.h:282: warning: `fh_setcmt_PIXSIZE1' defined but not used
+../fh_registry.h:282: warning: `fh_get_PIXSIZE1' defined but not used
+../fh_registry.h:282: warning: `fh_kw_PIXSIZE1' defined but not used
+../fh_registry.h:282: warning: `fh_cmt_PIXSIZE1' defined but not used
+../fh_registry.h:283: warning: `fh_set_PIXSIZE2' defined but not used
+../fh_registry.h:283: warning: `fh_setval_PIXSIZE2' defined but not used
+../fh_registry.h:283: warning: `fh_setcmt_PIXSIZE2' defined but not used
+../fh_registry.h:283: warning: `fh_get_PIXSIZE2' defined but not used
+../fh_registry.h:283: warning: `fh_kw_PIXSIZE2' defined but not used
+../fh_registry.h:283: warning: `fh_cmt_PIXSIZE2' defined but not used
+../fh_registry.h:284: warning: `fh_set_PIXSCAL1' defined but not used
+../fh_registry.h:284: warning: `fh_setval_PIXSCAL1' defined but not used
+../fh_registry.h:284: warning: `fh_setcmt_PIXSCAL1' defined but not used
+../fh_registry.h:284: warning: `fh_get_PIXSCAL1' defined but not used
+../fh_registry.h:284: warning: `fh_kw_PIXSCAL1' defined but not used
+../fh_registry.h:284: warning: `fh_cmt_PIXSCAL1' defined but not used
+../fh_registry.h:285: warning: `fh_set_PIXSCAL2' defined but not used
+../fh_registry.h:285: warning: `fh_setval_PIXSCAL2' defined but not used
+../fh_registry.h:285: warning: `fh_setcmt_PIXSCAL2' defined but not used
+../fh_registry.h:285: warning: `fh_get_PIXSCAL2' defined but not used
+../fh_registry.h:285: warning: `fh_kw_PIXSCAL2' defined but not used
+../fh_registry.h:285: warning: `fh_cmt_PIXSCAL2' defined but not used
+../fh_registry.h:286: warning: `fh_set_AMPLIST' defined but not used
+../fh_registry.h:286: warning: `fh_setval_AMPLIST' defined but not used
+../fh_registry.h:286: warning: `fh_setcmt_AMPLIST' defined but not used
+../fh_registry.h:286: warning: `fh_get_AMPLIST' defined but not used
+../fh_registry.h:286: warning: `fh_kw_AMPLIST' defined but not used
+../fh_registry.h:286: warning: `fh_cmt_AMPLIST' defined but not used
+../fh_registry.h:287: warning: `fh_set_AMPNAME' defined but not used
+../fh_registry.h:287: warning: `fh_setval_AMPNAME' defined but not used
+../fh_registry.h:287: warning: `fh_setcmt_AMPNAME' defined but not used
+../fh_registry.h:287: warning: `fh_get_AMPNAME' defined but not used
+../fh_registry.h:287: warning: `fh_kw_AMPNAME' defined but not used
+../fh_registry.h:287: warning: `fh_cmt_AMPNAME' defined but not used
+../fh_registry.h:288: warning: `fh_set_CCDSIZE' defined but not used
+../fh_registry.h:288: warning: `fh_setval_CCDSIZE' defined but not used
+../fh_registry.h:288: warning: `fh_setcmt_CCDSIZE' defined but not used
+../fh_registry.h:288: warning: `fh_get_CCDSIZE' defined but not used
+../fh_registry.h:288: warning: `fh_kw_CCDSIZE' defined but not used
+../fh_registry.h:288: warning: `fh_cmt_CCDSIZE' defined but not used
+../fh_registry.h:289: warning: `fh_set_DETSEC' defined but not used
+../fh_registry.h:289: warning: `fh_setval_DETSEC' defined but not used
+../fh_registry.h:289: warning: `fh_setcmt_DETSEC' defined but not used
+../fh_registry.h:289: warning: `fh_get_DETSEC' defined but not used
+../fh_registry.h:289: warning: `fh_kw_DETSEC' defined but not used
+../fh_registry.h:289: warning: `fh_cmt_DETSEC' defined but not used
+../fh_registry.h:290: warning: `fh_set_DETSECA' defined but not used
+../fh_registry.h:290: warning: `fh_setval_DETSECA' defined but not used
+../fh_registry.h:290: warning: `fh_setcmt_DETSECA' defined but not used
+../fh_registry.h:290: warning: `fh_get_DETSECA' defined but not used
+../fh_registry.h:290: warning: `fh_kw_DETSECA' defined but not used
+../fh_registry.h:290: warning: `fh_cmt_DETSECA' defined but not used
+../fh_registry.h:291: warning: `fh_set_DETSECB' defined but not used
+../fh_registry.h:291: warning: `fh_setval_DETSECB' defined but not used
+../fh_registry.h:291: warning: `fh_setcmt_DETSECB' defined but not used
+../fh_registry.h:291: warning: `fh_get_DETSECB' defined but not used
+../fh_registry.h:291: warning: `fh_kw_DETSECB' defined but not used
+../fh_registry.h:291: warning: `fh_cmt_DETSECB' defined but not used
+../fh_registry.h:292: warning: `fh_set_DETSECC' defined but not used
+../fh_registry.h:292: warning: `fh_setval_DETSECC' defined but not used
+../fh_registry.h:292: warning: `fh_setcmt_DETSECC' defined but not used
+../fh_registry.h:292: warning: `fh_get_DETSECC' defined but not used
+../fh_registry.h:292: warning: `fh_kw_DETSECC' defined but not used
+../fh_registry.h:292: warning: `fh_cmt_DETSECC' defined but not used
+../fh_registry.h:293: warning: `fh_set_DETSECD' defined but not used
+../fh_registry.h:293: warning: `fh_setval_DETSECD' defined but not used
+../fh_registry.h:293: warning: `fh_setcmt_DETSECD' defined but not used
+../fh_registry.h:293: warning: `fh_get_DETSECD' defined but not used
+../fh_registry.h:293: warning: `fh_kw_DETSECD' defined but not used
+../fh_registry.h:293: warning: `fh_cmt_DETSECD' defined but not used
+../fh_registry.h:294: warning: `fh_set_DATASEC' defined but not used
+../fh_registry.h:294: warning: `fh_setval_DATASEC' defined but not used
+../fh_registry.h:294: warning: `fh_setcmt_DATASEC' defined but not used
+../fh_registry.h:294: warning: `fh_get_DATASEC' defined but not used
+../fh_registry.h:294: warning: `fh_kw_DATASEC' defined but not used
+../fh_registry.h:294: warning: `fh_cmt_DATASEC' defined but not used
+../fh_registry.h:295: warning: `fh_set_BIASSEC' defined but not used
+../fh_registry.h:295: warning: `fh_setval_BIASSEC' defined but not used
+../fh_registry.h:295: warning: `fh_setcmt_BIASSEC' defined but not used
+../fh_registry.h:295: warning: `fh_get_BIASSEC' defined but not used
+../fh_registry.h:295: warning: `fh_kw_BIASSEC' defined but not used
+../fh_registry.h:295: warning: `fh_cmt_BIASSEC' defined but not used
+../fh_registry.h:296: warning: `fh_set_ASECA' defined but not used
+../fh_registry.h:296: warning: `fh_setval_ASECA' defined but not used
+../fh_registry.h:296: warning: `fh_setcmt_ASECA' defined but not used
+../fh_registry.h:296: warning: `fh_get_ASECA' defined but not used
+../fh_registry.h:296: warning: `fh_kw_ASECA' defined but not used
+../fh_registry.h:296: warning: `fh_cmt_ASECA' defined but not used
+../fh_registry.h:297: warning: `fh_set_ASECB' defined but not used
+../fh_registry.h:297: warning: `fh_setval_ASECB' defined but not used
+../fh_registry.h:297: warning: `fh_setcmt_ASECB' defined but not used
+../fh_registry.h:297: warning: `fh_get_ASECB' defined but not used
+../fh_registry.h:297: warning: `fh_kw_ASECB' defined but not used
+../fh_registry.h:297: warning: `fh_cmt_ASECB' defined but not used
+../fh_registry.h:298: warning: `fh_set_ASECC' defined but not used
+../fh_registry.h:298: warning: `fh_setval_ASECC' defined but not used
+../fh_registry.h:298: warning: `fh_setcmt_ASECC' defined but not used
+../fh_registry.h:298: warning: `fh_get_ASECC' defined but not used
+../fh_registry.h:298: warning: `fh_kw_ASECC' defined but not used
+../fh_registry.h:298: warning: `fh_cmt_ASECC' defined but not used
+../fh_registry.h:299: warning: `fh_set_ASECD' defined but not used
+../fh_registry.h:299: warning: `fh_setval_ASECD' defined but not used
+../fh_registry.h:299: warning: `fh_setcmt_ASECD' defined but not used
+../fh_registry.h:299: warning: `fh_get_ASECD' defined but not used
+../fh_registry.h:299: warning: `fh_kw_ASECD' defined but not used
+../fh_registry.h:299: warning: `fh_cmt_ASECD' defined but not used
+../fh_registry.h:300: warning: `fh_set_BSECA' defined but not used
+../fh_registry.h:300: warning: `fh_setval_BSECA' defined but not used
+../fh_registry.h:300: warning: `fh_setcmt_BSECA' defined but not used
+../fh_registry.h:300: warning: `fh_get_BSECA' defined but not used
+../fh_registry.h:300: warning: `fh_kw_BSECA' defined but not used
+../fh_registry.h:300: warning: `fh_cmt_BSECA' defined but not used
+../fh_registry.h:301: warning: `fh_set_BSECB' defined but not used
+../fh_registry.h:301: warning: `fh_setval_BSECB' defined but not used
+../fh_registry.h:301: warning: `fh_setcmt_BSECB' defined but not used
+../fh_registry.h:301: warning: `fh_get_BSECB' defined but not used
+../fh_registry.h:301: warning: `fh_kw_BSECB' defined but not used
+../fh_registry.h:301: warning: `fh_cmt_BSECB' defined but not used
+../fh_registry.h:302: warning: `fh_set_BSECC' defined but not used
+../fh_registry.h:302: warning: `fh_setval_BSECC' defined but not used
+../fh_registry.h:302: warning: `fh_setcmt_BSECC' defined but not used
+../fh_registry.h:302: warning: `fh_get_BSECC' defined but not used
+../fh_registry.h:302: warning: `fh_kw_BSECC' defined but not used
+../fh_registry.h:302: warning: `fh_cmt_BSECC' defined but not used
+../fh_registry.h:303: warning: `fh_set_BSECD' defined but not used
+../fh_registry.h:303: warning: `fh_setval_BSECD' defined but not used
+../fh_registry.h:303: warning: `fh_setcmt_BSECD' defined but not used
+../fh_registry.h:303: warning: `fh_get_BSECD' defined but not used
+../fh_registry.h:303: warning: `fh_kw_BSECD' defined but not used
+../fh_registry.h:303: warning: `fh_cmt_BSECD' defined but not used
+../fh_registry.h:304: warning: `fh_set_CSECA' defined but not used
+../fh_registry.h:304: warning: `fh_setval_CSECA' defined but not used
+../fh_registry.h:304: warning: `fh_setcmt_CSECA' defined but not used
+../fh_registry.h:304: warning: `fh_get_CSECA' defined but not used
+../fh_registry.h:304: warning: `fh_kw_CSECA' defined but not used
+../fh_registry.h:304: warning: `fh_cmt_CSECA' defined but not used
+../fh_registry.h:305: warning: `fh_set_CSECB' defined but not used
+../fh_registry.h:305: warning: `fh_setval_CSECB' defined but not used
+../fh_registry.h:305: warning: `fh_setcmt_CSECB' defined but not used
+../fh_registry.h:305: warning: `fh_get_CSECB' defined but not used
+../fh_registry.h:305: warning: `fh_kw_CSECB' defined but not used
+../fh_registry.h:305: warning: `fh_cmt_CSECB' defined but not used
+../fh_registry.h:306: warning: `fh_set_CSECC' defined but not used
+../fh_registry.h:306: warning: `fh_setval_CSECC' defined but not used
+../fh_registry.h:306: warning: `fh_setcmt_CSECC' defined but not used
+../fh_registry.h:306: warning: `fh_get_CSECC' defined but not used
+../fh_registry.h:306: warning: `fh_kw_CSECC' defined but not used
+../fh_registry.h:306: warning: `fh_cmt_CSECC' defined but not used
+../fh_registry.h:307: warning: `fh_set_CSECD' defined but not used
+../fh_registry.h:307: warning: `fh_setval_CSECD' defined but not used
+../fh_registry.h:307: warning: `fh_setcmt_CSECD' defined but not used
+../fh_registry.h:307: warning: `fh_get_CSECD' defined but not used
+../fh_registry.h:307: warning: `fh_kw_CSECD' defined but not used
+../fh_registry.h:307: warning: `fh_cmt_CSECD' defined but not used
+../fh_registry.h:308: warning: `fh_set_DSECA' defined but not used
+../fh_registry.h:308: warning: `fh_setval_DSECA' defined but not used
+../fh_registry.h:308: warning: `fh_setcmt_DSECA' defined but not used
+../fh_registry.h:308: warning: `fh_get_DSECA' defined but not used
+../fh_registry.h:308: warning: `fh_kw_DSECA' defined but not used
+../fh_registry.h:308: warning: `fh_cmt_DSECA' defined but not used
+../fh_registry.h:309: warning: `fh_set_DSECB' defined but not used
+../fh_registry.h:309: warning: `fh_setval_DSECB' defined but not used
+../fh_registry.h:309: warning: `fh_setcmt_DSECB' defined but not used
+../fh_registry.h:309: warning: `fh_get_DSECB' defined but not used
+../fh_registry.h:309: warning: `fh_kw_DSECB' defined but not used
+../fh_registry.h:309: warning: `fh_cmt_DSECB' defined but not used
+../fh_registry.h:310: warning: `fh_set_DSECC' defined but not used
+../fh_registry.h:310: warning: `fh_setval_DSECC' defined but not used
+../fh_registry.h:310: warning: `fh_setcmt_DSECC' defined but not used
+../fh_registry.h:310: warning: `fh_get_DSECC' defined but not used
+../fh_registry.h:310: warning: `fh_kw_DSECC' defined but not used
+../fh_registry.h:310: warning: `fh_cmt_DSECC' defined but not used
+../fh_registry.h:311: warning: `fh_set_DSECD' defined but not used
+../fh_registry.h:311: warning: `fh_setval_DSECD' defined but not used
+../fh_registry.h:311: warning: `fh_setcmt_DSECD' defined but not used
+../fh_registry.h:311: warning: `fh_get_DSECD' defined but not used
+../fh_registry.h:311: warning: `fh_kw_DSECD' defined but not used
+../fh_registry.h:311: warning: `fh_cmt_DSECD' defined but not used
+../fh_registry.h:312: warning: `fh_set_TSECA' defined but not used
+../fh_registry.h:312: warning: `fh_setval_TSECA' defined but not used
+../fh_registry.h:312: warning: `fh_setcmt_TSECA' defined but not used
+../fh_registry.h:312: warning: `fh_get_TSECA' defined but not used
+../fh_registry.h:312: warning: `fh_kw_TSECA' defined but not used
+../fh_registry.h:312: warning: `fh_cmt_TSECA' defined but not used
+../fh_registry.h:313: warning: `fh_set_TSECB' defined but not used
+../fh_registry.h:313: warning: `fh_setval_TSECB' defined but not used
+../fh_registry.h:313: warning: `fh_setcmt_TSECB' defined but not used
+../fh_registry.h:313: warning: `fh_get_TSECB' defined but not used
+../fh_registry.h:313: warning: `fh_kw_TSECB' defined but not used
+../fh_registry.h:313: warning: `fh_cmt_TSECB' defined but not used
+../fh_registry.h:314: warning: `fh_set_TSECC' defined but not used
+../fh_registry.h:314: warning: `fh_setval_TSECC' defined but not used
+../fh_registry.h:314: warning: `fh_setcmt_TSECC' defined but not used
+../fh_registry.h:314: warning: `fh_get_TSECC' defined but not used
+../fh_registry.h:314: warning: `fh_kw_TSECC' defined but not used
+../fh_registry.h:314: warning: `fh_cmt_TSECC' defined but not used
+../fh_registry.h:315: warning: `fh_set_TSECD' defined but not used
+../fh_registry.h:315: warning: `fh_setval_TSECD' defined but not used
+../fh_registry.h:315: warning: `fh_setcmt_TSECD' defined but not used
+../fh_registry.h:315: warning: `fh_get_TSECD' defined but not used
+../fh_registry.h:315: warning: `fh_kw_TSECD' defined but not used
+../fh_registry.h:315: warning: `fh_cmt_TSECD' defined but not used
+../fh_registry.h:316: warning: `fh_set_CCDNAME' defined but not used
+../fh_registry.h:316: warning: `fh_setval_CCDNAME' defined but not used
+../fh_registry.h:316: warning: `fh_setcmt_CCDNAME' defined but not used
+../fh_registry.h:316: warning: `fh_get_CCDNAME' defined but not used
+../fh_registry.h:316: warning: `fh_kw_CCDNAME' defined but not used
+../fh_registry.h:316: warning: `fh_cmt_CCDNAME' defined but not used
+../fh_registry.h:317: warning: `fh_set_CCDNICK' defined but not used
+../fh_registry.h:317: warning: `fh_setval_CCDNICK' defined but not used
+../fh_registry.h:317: warning: `fh_setcmt_CCDNICK' defined but not used
+../fh_registry.h:317: warning: `fh_get_CCDNICK' defined but not used
+../fh_registry.h:317: warning: `fh_kw_CCDNICK' defined but not used
+../fh_registry.h:317: warning: `fh_cmt_CCDNICK' defined but not used
+../fh_registry.h:318: warning: `fh_set_MAXLIN' defined but not used
+../fh_registry.h:318: warning: `fh_setval_MAXLIN' defined but not used
+../fh_registry.h:318: warning: `fh_setcmt_MAXLIN' defined but not used
+../fh_registry.h:318: warning: `fh_get_MAXLIN' defined but not used
+../fh_registry.h:318: warning: `fh_kw_MAXLIN' defined but not used
+../fh_registry.h:318: warning: `fh_cmt_MAXLIN' defined but not used
+../fh_registry.h:319: warning: `fh_set_MAXLINA' defined but not used
+../fh_registry.h:319: warning: `fh_setval_MAXLINA' defined but not used
+../fh_registry.h:319: warning: `fh_setcmt_MAXLINA' defined but not used
+../fh_registry.h:319: warning: `fh_get_MAXLINA' defined but not used
+../fh_registry.h:319: warning: `fh_kw_MAXLINA' defined but not used
+../fh_registry.h:319: warning: `fh_cmt_MAXLINA' defined but not used
+../fh_registry.h:320: warning: `fh_set_MAXLINB' defined but not used
+../fh_registry.h:320: warning: `fh_setval_MAXLINB' defined but not used
+../fh_registry.h:320: warning: `fh_setcmt_MAXLINB' defined but not used
+../fh_registry.h:320: warning: `fh_get_MAXLINB' defined but not used
+../fh_registry.h:320: warning: `fh_kw_MAXLINB' defined but not used
+../fh_registry.h:320: warning: `fh_cmt_MAXLINB' defined but not used
+../fh_registry.h:321: warning: `fh_set_MAXLINC' defined but not used
+../fh_registry.h:321: warning: `fh_setval_MAXLINC' defined but not used
+../fh_registry.h:321: warning: `fh_setcmt_MAXLINC' defined but not used
+../fh_registry.h:321: warning: `fh_get_MAXLINC' defined but not used
+../fh_registry.h:321: warning: `fh_kw_MAXLINC' defined but not used
+../fh_registry.h:321: warning: `fh_cmt_MAXLINC' defined but not used
+../fh_registry.h:322: warning: `fh_set_MAXLIND' defined but not used
+../fh_registry.h:322: warning: `fh_setval_MAXLIND' defined but not used
+../fh_registry.h:322: warning: `fh_setcmt_MAXLIND' defined but not used
+../fh_registry.h:322: warning: `fh_get_MAXLIND' defined but not used
+../fh_registry.h:322: warning: `fh_kw_MAXLIND' defined but not used
+../fh_registry.h:322: warning: `fh_cmt_MAXLIND' defined but not used
+../fh_registry.h:323: warning: `fh_set_SATURATE' defined but not used
+../fh_registry.h:323: warning: `fh_setval_SATURATE' defined but not used
+../fh_registry.h:323: warning: `fh_setcmt_SATURATE' defined but not used
+../fh_registry.h:323: warning: `fh_get_SATURATE' defined but not used
+../fh_registry.h:323: warning: `fh_kw_SATURATE' defined but not used
+../fh_registry.h:323: warning: `fh_cmt_SATURATE' defined but not used
+../fh_registry.h:324: warning: `fh_set_GAIN' defined but not used
+../fh_registry.h:324: warning: `fh_setval_GAIN' defined but not used
+../fh_registry.h:324: warning: `fh_setcmt_GAIN' defined but not used
+../fh_registry.h:324: warning: `fh_get_GAIN' defined but not used
+../fh_registry.h:324: warning: `fh_kw_GAIN' defined but not used
+../fh_registry.h:324: warning: `fh_cmt_GAIN' defined but not used
+../fh_registry.h:325: warning: `fh_set_GAINA' defined but not used
+../fh_registry.h:325: warning: `fh_setval_GAINA' defined but not used
+../fh_registry.h:325: warning: `fh_setcmt_GAINA' defined but not used
+../fh_registry.h:325: warning: `fh_get_GAINA' defined but not used
+../fh_registry.h:325: warning: `fh_kw_GAINA' defined but not used
+../fh_registry.h:325: warning: `fh_cmt_GAINA' defined but not used
+../fh_registry.h:326: warning: `fh_set_GAINB' defined but not used
+../fh_registry.h:326: warning: `fh_setval_GAINB' defined but not used
+../fh_registry.h:326: warning: `fh_setcmt_GAINB' defined but not used
+../fh_registry.h:326: warning: `fh_get_GAINB' defined but not used
+../fh_registry.h:326: warning: `fh_kw_GAINB' defined but not used
+../fh_registry.h:326: warning: `fh_cmt_GAINB' defined but not used
+../fh_registry.h:327: warning: `fh_set_GAINC' defined but not used
+../fh_registry.h:327: warning: `fh_setval_GAINC' defined but not used
+../fh_registry.h:327: warning: `fh_setcmt_GAINC' defined but not used
+../fh_registry.h:327: warning: `fh_get_GAINC' defined but not used
+../fh_registry.h:327: warning: `fh_kw_GAINC' defined but not used
+../fh_registry.h:327: warning: `fh_cmt_GAINC' defined but not used
+../fh_registry.h:328: warning: `fh_set_GAIND' defined but not used
+../fh_registry.h:328: warning: `fh_setval_GAIND' defined but not used
+../fh_registry.h:328: warning: `fh_setcmt_GAIND' defined but not used
+../fh_registry.h:328: warning: `fh_get_GAIND' defined but not used
+../fh_registry.h:328: warning: `fh_kw_GAIND' defined but not used
+../fh_registry.h:328: warning: `fh_cmt_GAIND' defined but not used
+../fh_registry.h:329: warning: `fh_set_RDNOISE' defined but not used
+../fh_registry.h:329: warning: `fh_setval_RDNOISE' defined but not used
+../fh_registry.h:329: warning: `fh_setcmt_RDNOISE' defined but not used
+../fh_registry.h:329: warning: `fh_get_RDNOISE' defined but not used
+../fh_registry.h:329: warning: `fh_kw_RDNOISE' defined but not used
+../fh_registry.h:329: warning: `fh_cmt_RDNOISE' defined but not used
+../fh_registry.h:330: warning: `fh_set_RDNOISEA' defined but not used
+../fh_registry.h:330: warning: `fh_setval_RDNOISEA' defined but not used
+../fh_registry.h:330: warning: `fh_setcmt_RDNOISEA' defined but not used
+../fh_registry.h:330: warning: `fh_get_RDNOISEA' defined but not used
+../fh_registry.h:330: warning: `fh_kw_RDNOISEA' defined but not used
+../fh_registry.h:330: warning: `fh_cmt_RDNOISEA' defined but not used
+../fh_registry.h:331: warning: `fh_set_RDNOISEB' defined but not used
+../fh_registry.h:331: warning: `fh_setval_RDNOISEB' defined but not used
+../fh_registry.h:331: warning: `fh_setcmt_RDNOISEB' defined but not used
+../fh_registry.h:331: warning: `fh_get_RDNOISEB' defined but not used
+../fh_registry.h:331: warning: `fh_kw_RDNOISEB' defined but not used
+../fh_registry.h:331: warning: `fh_cmt_RDNOISEB' defined but not used
+../fh_registry.h:332: warning: `fh_set_RDNOISEC' defined but not used
+../fh_registry.h:332: warning: `fh_setval_RDNOISEC' defined but not used
+../fh_registry.h:332: warning: `fh_setcmt_RDNOISEC' defined but not used
+../fh_registry.h:332: warning: `fh_get_RDNOISEC' defined but not used
+../fh_registry.h:332: warning: `fh_kw_RDNOISEC' defined but not used
+../fh_registry.h:332: warning: `fh_cmt_RDNOISEC' defined but not used
+../fh_registry.h:333: warning: `fh_set_RDNOISED' defined but not used
+../fh_registry.h:333: warning: `fh_setval_RDNOISED' defined but not used
+../fh_registry.h:333: warning: `fh_setcmt_RDNOISED' defined but not used
+../fh_registry.h:333: warning: `fh_get_RDNOISED' defined but not used
+../fh_registry.h:333: warning: `fh_kw_RDNOISED' defined but not used
+../fh_registry.h:333: warning: `fh_cmt_RDNOISED' defined but not used
+../fh_registry.h:334: warning: `fh_set_DCURRENT' defined but not used
+../fh_registry.h:334: warning: `fh_setval_DCURRENT' defined but not used
+../fh_registry.h:334: warning: `fh_setcmt_DCURRENT' defined but not used
+../fh_registry.h:334: warning: `fh_get_DCURRENT' defined but not used
+../fh_registry.h:334: warning: `fh_kw_DCURRENT' defined but not used
+../fh_registry.h:334: warning: `fh_cmt_DCURRENT' defined but not used
+../fh_registry.h:335: warning: `fh_set_DARKCUR' defined but not used
+../fh_registry.h:335: warning: `fh_setval_DARKCUR' defined but not used
+../fh_registry.h:335: warning: `fh_setcmt_DARKCUR' defined but not used
+../fh_registry.h:335: warning: `fh_get_DARKCUR' defined but not used
+../fh_registry.h:335: warning: `fh_kw_DARKCUR' defined but not used
+../fh_registry.h:335: warning: `fh_cmt_DARKCUR' defined but not used
+../fh_registry.h:336: warning: `fh_set_QEPOINTS' defined but not used
+../fh_registry.h:336: warning: `fh_setval_QEPOINTS' defined but not used
+../fh_registry.h:336: warning: `fh_setcmt_QEPOINTS' defined but not used
+../fh_registry.h:336: warning: `fh_get_QEPOINTS' defined but not used
+../fh_registry.h:336: warning: `fh_kw_QEPOINTS' defined but not used
+../fh_registry.h:336: warning: `fh_cmt_QEPOINTS' defined but not used
+../fh_registry.h:337: warning: `fh_set_CONSWV' defined but not used
+../fh_registry.h:337: warning: `fh_setval_CONSWV' defined but not used
+../fh_registry.h:337: warning: `fh_setcmt_CONSWV' defined but not used
+../fh_registry.h:337: warning: `fh_get_CONSWV' defined but not used
+../fh_registry.h:337: warning: `fh_kw_CONSWV' defined but not used
+../fh_registry.h:337: warning: `fh_cmt_CONSWV' defined but not used
+../fh_registry.h:338: warning: `fh_set_DETSTAT' defined but not used
+../fh_registry.h:338: warning: `fh_setval_DETSTAT' defined but not used
+../fh_registry.h:338: warning: `fh_setcmt_DETSTAT' defined but not used
+../fh_registry.h:338: warning: `fh_get_DETSTAT' defined but not used
+../fh_registry.h:338: warning: `fh_kw_DETSTAT' defined but not used
+../fh_registry.h:338: warning: `fh_cmt_DETSTAT' defined but not used
+../fh_registry.h:339: warning: `fh_set_DETTEM' defined but not used
+../fh_registry.h:339: warning: `fh_setval_DETTEM' defined but not used
+../fh_registry.h:339: warning: `fh_setcmt_DETTEM' defined but not used
+../fh_registry.h:339: warning: `fh_get_DETTEM' defined but not used
+../fh_registry.h:339: warning: `fh_kw_DETTEM' defined but not used
+../fh_registry.h:339: warning: `fh_cmt_DETTEM' defined but not used
+../fh_registry.h:385: warning: `fh_set_DATE_OBS' defined but not used
+../fh_registry.h:385: warning: `fh_setval_DATE_OBS' defined but not used
+../fh_registry.h:385: warning: `fh_setcmt_DATE_OBS' defined but not used
+../fh_registry.h:385: warning: `fh_get_DATE_OBS' defined but not used
+../fh_registry.h:385: warning: `fh_kw_DATE_OBS' defined but not used
+../fh_registry.h:385: warning: `fh_cmt_DATE_OBS' defined but not used
+../fh_registry.h:386: warning: `fh_set_UTC_OBS' defined but not used
+../fh_registry.h:386: warning: `fh_setval_UTC_OBS' defined but not used
+../fh_registry.h:386: warning: `fh_setcmt_UTC_OBS' defined but not used
+../fh_registry.h:386: warning: `fh_get_UTC_OBS' defined but not used
+../fh_registry.h:386: warning: `fh_kw_UTC_OBS' defined but not used
+../fh_registry.h:386: warning: `fh_cmt_UTC_OBS' defined but not used
+../fh_registry.h:387: warning: `fh_set_MJD_OBS' defined but not used
+../fh_registry.h:387: warning: `fh_setval_MJD_OBS' defined but not used
+../fh_registry.h:387: warning: `fh_setcmt_MJD_OBS' defined but not used
+../fh_registry.h:387: warning: `fh_get_MJD_OBS' defined but not used
+../fh_registry.h:387: warning: `fh_kw_MJD_OBS' defined but not used
+../fh_registry.h:387: warning: `fh_cmt_MJD_OBS' defined but not used
+../fh_registry.h:388: warning: `fh_set_LST_OBS' defined but not used
+../fh_registry.h:388: warning: `fh_setval_LST_OBS' defined but not used
+../fh_registry.h:388: warning: `fh_setcmt_LST_OBS' defined but not used
+../fh_registry.h:388: warning: `fh_get_LST_OBS' defined but not used
+../fh_registry.h:388: warning: `fh_kw_LST_OBS' defined but not used
+../fh_registry.h:388: warning: `fh_cmt_LST_OBS' defined but not used
+../fh_registry.h:391: warning: `fh_set_CMTTCS1' defined but not used
+../fh_registry.h:392: warning: `fh_set_CMTTCS2' defined but not used
+../fh_registry.h:393: warning: `fh_set_CMTTCS3' defined but not used
+../fh_registry.h:394: warning: `fh_set_CMTTCS4' defined but not used
+../fh_registry.h:395: warning: `fh_set_TELESCOP' defined but not used
+../fh_registry.h:395: warning: `fh_setval_TELESCOP' defined but not used
+../fh_registry.h:395: warning: `fh_setcmt_TELESCOP' defined but not used
+../fh_registry.h:395: warning: `fh_get_TELESCOP' defined but not used
+../fh_registry.h:395: warning: `fh_kw_TELESCOP' defined but not used
+../fh_registry.h:395: warning: `fh_cmt_TELESCOP' defined but not used
+../fh_registry.h:396: warning: `fh_set_ORIGIN' defined but not used
+../fh_registry.h:396: warning: `fh_setval_ORIGIN' defined but not used
+../fh_registry.h:396: warning: `fh_setcmt_ORIGIN' defined but not used
+../fh_registry.h:396: warning: `fh_get_ORIGIN' defined but not used
+../fh_registry.h:396: warning: `fh_kw_ORIGIN' defined but not used
+../fh_registry.h:396: warning: `fh_cmt_ORIGIN' defined but not used
+../fh_registry.h:397: warning: `fh_set_LATITUDE' defined but not used
+../fh_registry.h:397: warning: `fh_setval_LATITUDE' defined but not used
+../fh_registry.h:397: warning: `fh_setcmt_LATITUDE' defined but not used
+../fh_registry.h:397: warning: `fh_get_LATITUDE' defined but not used
+../fh_registry.h:397: warning: `fh_kw_LATITUDE' defined but not used
+../fh_registry.h:397: warning: `fh_cmt_LATITUDE' defined but not used
+../fh_registry.h:398: warning: `fh_set_LONGITUD' defined but not used
+../fh_registry.h:398: warning: `fh_setval_LONGITUD' defined but not used
+../fh_registry.h:398: warning: `fh_setcmt_LONGITUD' defined but not used
+../fh_registry.h:398: warning: `fh_get_LONGITUD' defined but not used
+../fh_registry.h:398: warning: `fh_kw_LONGITUD' defined but not used
+../fh_registry.h:398: warning: `fh_cmt_LONGITUD' defined but not used
+../fh_registry.h:399: warning: `fh_set_TELSTAT' defined but not used
+../fh_registry.h:399: warning: `fh_setval_TELSTAT' defined but not used
+../fh_registry.h:399: warning: `fh_setcmt_TELSTAT' defined but not used
+../fh_registry.h:399: warning: `fh_get_TELSTAT' defined but not used
+../fh_registry.h:399: warning: `fh_kw_TELSTAT' defined but not used
+../fh_registry.h:399: warning: `fh_cmt_TELSTAT' defined but not used
+../fh_registry.h:400: warning: `fh_set_TIMESYS' defined but not used
+../fh_registry.h:400: warning: `fh_setval_TIMESYS' defined but not used
+../fh_registry.h:400: warning: `fh_setcmt_TIMESYS' defined but not used
+../fh_registry.h:400: warning: `fh_get_TIMESYS' defined but not used
+../fh_registry.h:400: warning: `fh_kw_TIMESYS' defined but not used
+../fh_registry.h:400: warning: `fh_cmt_TIMESYS' defined but not used
+../fh_registry.h:402: warning: `fh_set_UTIME' defined but not used
+../fh_registry.h:402: warning: `fh_setval_UTIME' defined but not used
+../fh_registry.h:402: warning: `fh_setcmt_UTIME' defined but not used
+../fh_registry.h:402: warning: `fh_get_UTIME' defined but not used
+../fh_registry.h:402: warning: `fh_kw_UTIME' defined but not used
+../fh_registry.h:402: warning: `fh_cmt_UTIME' defined but not used
+../fh_registry.h:403: warning: `fh_set_MJDATE' defined but not used
+../fh_registry.h:403: warning: `fh_setval_MJDATE' defined but not used
+../fh_registry.h:403: warning: `fh_setcmt_MJDATE' defined but not used
+../fh_registry.h:403: warning: `fh_get_MJDATE' defined but not used
+../fh_registry.h:403: warning: `fh_kw_MJDATE' defined but not used
+../fh_registry.h:403: warning: `fh_cmt_MJDATE' defined but not used
+../fh_registry.h:404: warning: `fh_set_SIDTIME' defined but not used
+../fh_registry.h:404: warning: `fh_setval_SIDTIME' defined but not used
+../fh_registry.h:404: warning: `fh_setcmt_SIDTIME' defined but not used
+../fh_registry.h:404: warning: `fh_get_SIDTIME' defined but not used
+../fh_registry.h:404: warning: `fh_kw_SIDTIME' defined but not used
+../fh_registry.h:404: warning: `fh_cmt_SIDTIME' defined but not used
+../fh_registry.h:405: warning: `fh_set_EPOCH' defined but not used
+../fh_registry.h:405: warning: `fh_setcmt_EPOCH' defined but not used
+../fh_registry.h:405: warning: `fh_kw_EPOCH' defined but not used
+../fh_registry.h:405: warning: `fh_cmt_EPOCH' defined but not used
+../fh_registry.h:406: warning: `fh_set_EQUINOX' defined but not used
+../fh_registry.h:406: warning: `fh_setcmt_EQUINOX' defined but not used
+../fh_registry.h:406: warning: `fh_kw_EQUINOX' defined but not used
+../fh_registry.h:406: warning: `fh_cmt_EQUINOX' defined but not used
+../fh_registry.h:407: warning: `fh_set_RADECSYS' defined but not used
+../fh_registry.h:407: warning: `fh_setval_RADECSYS' defined but not used
+../fh_registry.h:407: warning: `fh_setcmt_RADECSYS' defined but not used
+../fh_registry.h:407: warning: `fh_get_RADECSYS' defined but not used
+../fh_registry.h:407: warning: `fh_kw_RADECSYS' defined but not used
+../fh_registry.h:407: warning: `fh_cmt_RADECSYS' defined but not used
+../fh_registry.h:408: warning: `fh_set_RA' defined but not used
+../fh_registry.h:408: warning: `fh_setval_RA' defined but not used
+../fh_registry.h:408: warning: `fh_setcmt_RA' defined but not used
+../fh_registry.h:408: warning: `fh_get_RA' defined but not used
+../fh_registry.h:408: warning: `fh_kw_RA' defined but not used
+../fh_registry.h:408: warning: `fh_cmt_RA' defined but not used
+../fh_registry.h:409: warning: `fh_set_DEC' defined but not used
+../fh_registry.h:409: warning: `fh_setval_DEC' defined but not used
+../fh_registry.h:409: warning: `fh_setcmt_DEC' defined but not used
+../fh_registry.h:409: warning: `fh_get_DEC' defined but not used
+../fh_registry.h:409: warning: `fh_kw_DEC' defined but not used
+../fh_registry.h:409: warning: `fh_cmt_DEC' defined but not used
+../fh_registry.h:410: warning: `fh_set_RA_DEG' defined but not used
+../fh_registry.h:410: warning: `fh_setval_RA_DEG' defined but not used
+../fh_registry.h:410: warning: `fh_setcmt_RA_DEG' defined but not used
+../fh_registry.h:410: warning: `fh_get_RA_DEG' defined but not used
+../fh_registry.h:410: warning: `fh_kw_RA_DEG' defined but not used
+../fh_registry.h:410: warning: `fh_cmt_RA_DEG' defined but not used
+../fh_registry.h:411: warning: `fh_set_DEC_DEG' defined but not used
+../fh_registry.h:411: warning: `fh_setval_DEC_DEG' defined but not used
+../fh_registry.h:411: warning: `fh_setcmt_DEC_DEG' defined but not used
+../fh_registry.h:411: warning: `fh_get_DEC_DEG' defined but not used
+../fh_registry.h:411: warning: `fh_kw_DEC_DEG' defined but not used
+../fh_registry.h:411: warning: `fh_cmt_DEC_DEG' defined but not used
+../fh_registry.h:413: warning: `fh_set_CRVAL1' defined but not used
+../fh_registry.h:413: warning: `fh_setval_CRVAL1' defined but not used
+../fh_registry.h:413: warning: `fh_setcmt_CRVAL1' defined but not used
+../fh_registry.h:413: warning: `fh_get_CRVAL1' defined but not used
+../fh_registry.h:413: warning: `fh_kw_CRVAL1' defined but not used
+../fh_registry.h:413: warning: `fh_cmt_CRVAL1' defined but not used
+../fh_registry.h:414: warning: `fh_set_CRVAL2' defined but not used
+../fh_registry.h:414: warning: `fh_setval_CRVAL2' defined but not used
+../fh_registry.h:414: warning: `fh_setcmt_CRVAL2' defined but not used
+../fh_registry.h:414: warning: `fh_get_CRVAL2' defined but not used
+../fh_registry.h:414: warning: `fh_kw_CRVAL2' defined but not used
+../fh_registry.h:414: warning: `fh_cmt_CRVAL2' defined but not used
+../fh_registry.h:415: warning: `fh_set_CTYPE1' defined but not used
+../fh_registry.h:415: warning: `fh_setval_CTYPE1' defined but not used
+../fh_registry.h:415: warning: `fh_setcmt_CTYPE1' defined but not used
+../fh_registry.h:415: warning: `fh_get_CTYPE1' defined but not used
+../fh_registry.h:415: warning: `fh_kw_CTYPE1' defined but not used
+../fh_registry.h:415: warning: `fh_cmt_CTYPE1' defined but not used
+../fh_registry.h:416: warning: `fh_set_CTYPE2' defined but not used
+../fh_registry.h:416: warning: `fh_setval_CTYPE2' defined but not used
+../fh_registry.h:416: warning: `fh_setcmt_CTYPE2' defined but not used
+../fh_registry.h:416: warning: `fh_get_CTYPE2' defined but not used
+../fh_registry.h:416: warning: `fh_kw_CTYPE2' defined but not used
+../fh_registry.h:416: warning: `fh_cmt_CTYPE2' defined but not used
+../fh_registry.h:417: warning: `fh_set_CRPIX1' defined but not used
+../fh_registry.h:417: warning: `fh_setval_CRPIX1' defined but not used
+../fh_registry.h:417: warning: `fh_setcmt_CRPIX1' defined but not used
+../fh_registry.h:417: warning: `fh_get_CRPIX1' defined but not used
+../fh_registry.h:417: warning: `fh_kw_CRPIX1' defined but not used
+../fh_registry.h:417: warning: `fh_cmt_CRPIX1' defined but not used
+../fh_registry.h:418: warning: `fh_set_CRPIX2' defined but not used
+../fh_registry.h:418: warning: `fh_setval_CRPIX2' defined but not used
+../fh_registry.h:418: warning: `fh_setcmt_CRPIX2' defined but not used
+../fh_registry.h:418: warning: `fh_get_CRPIX2' defined but not used
+../fh_registry.h:418: warning: `fh_kw_CRPIX2' defined but not used
+../fh_registry.h:418: warning: `fh_cmt_CRPIX2' defined but not used
+../fh_registry.h:419: warning: `fh_set_CD1_1' defined but not used
+../fh_registry.h:419: warning: `fh_setval_CD1_1' defined but not used
+../fh_registry.h:419: warning: `fh_setcmt_CD1_1' defined but not used
+../fh_registry.h:419: warning: `fh_get_CD1_1' defined but not used
+../fh_registry.h:419: warning: `fh_kw_CD1_1' defined but not used
+../fh_registry.h:419: warning: `fh_cmt_CD1_1' defined but not used
+../fh_registry.h:420: warning: `fh_set_CD1_2' defined but not used
+../fh_registry.h:420: warning: `fh_setval_CD1_2' defined but not used
+../fh_registry.h:420: warning: `fh_setcmt_CD1_2' defined but not used
+../fh_registry.h:420: warning: `fh_get_CD1_2' defined but not used
+../fh_registry.h:420: warning: `fh_kw_CD1_2' defined but not used
+../fh_registry.h:420: warning: `fh_cmt_CD1_2' defined but not used
+../fh_registry.h:421: warning: `fh_set_CD2_1' defined but not used
+../fh_registry.h:421: warning: `fh_setval_CD2_1' defined but not used
+../fh_registry.h:421: warning: `fh_setcmt_CD2_1' defined but not used
+../fh_registry.h:421: warning: `fh_get_CD2_1' defined but not used
+../fh_registry.h:421: warning: `fh_kw_CD2_1' defined but not used
+../fh_registry.h:421: warning: `fh_cmt_CD2_1' defined but not used
+../fh_registry.h:422: warning: `fh_set_CD2_2' defined but not used
+../fh_registry.h:422: warning: `fh_setval_CD2_2' defined but not used
+../fh_registry.h:422: warning: `fh_setcmt_CD2_2' defined but not used
+../fh_registry.h:422: warning: `fh_get_CD2_2' defined but not used
+../fh_registry.h:422: warning: `fh_kw_CD2_2' defined but not used
+../fh_registry.h:422: warning: `fh_cmt_CD2_2' defined but not used
+../fh_registry.h:424: warning: `fh_set_NGUIDER' defined but not used
+../fh_registry.h:424: warning: `fh_setval_NGUIDER' defined but not used
+../fh_registry.h:424: warning: `fh_setcmt_NGUIDER' defined but not used
+../fh_registry.h:424: warning: `fh_get_NGUIDER' defined but not used
+../fh_registry.h:424: warning: `fh_kw_NGUIDER' defined but not used
+../fh_registry.h:424: warning: `fh_cmt_NGUIDER' defined but not used
+../fh_registry.h:425: warning: `fh_set_NGUISTAR' defined but not used
+../fh_registry.h:425: warning: `fh_setval_NGUISTAR' defined but not used
+../fh_registry.h:425: warning: `fh_setcmt_NGUISTAR' defined but not used
+../fh_registry.h:425: warning: `fh_get_NGUISTAR' defined but not used
+../fh_registry.h:425: warning: `fh_kw_NGUISTAR' defined but not used
+../fh_registry.h:425: warning: `fh_cmt_NGUISTAR' defined but not used
+../fh_registry.h:426: warning: `fh_set_GUINAME' defined but not used
+../fh_registry.h:426: warning: `fh_setval_GUINAME' defined but not used
+../fh_registry.h:426: warning: `fh_setcmt_GUINAME' defined but not used
+../fh_registry.h:426: warning: `fh_get_GUINAME' defined but not used
+../fh_registry.h:426: warning: `fh_kw_GUINAME' defined but not used
+../fh_registry.h:426: warning: `fh_cmt_GUINAME' defined but not used
+../fh_registry.h:427: warning: `fh_set_GUIEQUIN' defined but not used
+../fh_registry.h:427: warning: `fh_setcmt_GUIEQUIN' defined but not used
+../fh_registry.h:427: warning: `fh_kw_GUIEQUIN' defined but not used
+../fh_registry.h:427: warning: `fh_cmt_GUIEQUIN' defined but not used
+../fh_registry.h:428: warning: `fh_set_GUIRADEC' defined but not used
+../fh_registry.h:428: warning: `fh_setval_GUIRADEC' defined but not used
+../fh_registry.h:428: warning: `fh_setcmt_GUIRADEC' defined but not used
+../fh_registry.h:428: warning: `fh_get_GUIRADEC' defined but not used
+../fh_registry.h:428: warning: `fh_kw_GUIRADEC' defined but not used
+../fh_registry.h:428: warning: `fh_cmt_GUIRADEC' defined but not used
+../fh_registry.h:429: warning: `fh_set_GUIRA' defined but not used
+../fh_registry.h:429: warning: `fh_setval_GUIRA' defined but not used
+../fh_registry.h:429: warning: `fh_setcmt_GUIRA' defined but not used
+../fh_registry.h:429: warning: `fh_get_GUIRA' defined but not used
+../fh_registry.h:429: warning: `fh_kw_GUIRA' defined but not used
+../fh_registry.h:429: warning: `fh_cmt_GUIRA' defined but not used
+../fh_registry.h:430: warning: `fh_set_GUIDEC' defined but not used
+../fh_registry.h:430: warning: `fh_setval_GUIDEC' defined but not used
+../fh_registry.h:430: warning: `fh_setcmt_GUIDEC' defined but not used
+../fh_registry.h:430: warning: `fh_get_GUIDEC' defined but not used
+../fh_registry.h:430: warning: `fh_kw_GUIDEC' defined but not used
+../fh_registry.h:430: warning: `fh_cmt_GUIDEC' defined but not used
+../fh_registry.h:431: warning: `fh_set_GUIRAPM' defined but not used
+../fh_registry.h:431: warning: `fh_setval_GUIRAPM' defined but not used
+../fh_registry.h:431: warning: `fh_setcmt_GUIRAPM' defined but not used
+../fh_registry.h:431: warning: `fh_get_GUIRAPM' defined but not used
+../fh_registry.h:431: warning: `fh_kw_GUIRAPM' defined but not used
+../fh_registry.h:431: warning: `fh_cmt_GUIRAPM' defined but not used
+../fh_registry.h:432: warning: `fh_set_GUIDECPM' defined but not used
+../fh_registry.h:432: warning: `fh_setval_GUIDECPM' defined but not used
+../fh_registry.h:432: warning: `fh_setcmt_GUIDECPM' defined but not used
+../fh_registry.h:432: warning: `fh_get_GUIDECPM' defined but not used
+../fh_registry.h:432: warning: `fh_kw_GUIDECPM' defined but not used
+../fh_registry.h:432: warning: `fh_cmt_GUIDECPM' defined but not used
+../fh_registry.h:433: warning: `fh_set_GUIOBJN' defined but not used
+../fh_registry.h:433: warning: `fh_setval_GUIOBJN' defined but not used
+../fh_registry.h:433: warning: `fh_setcmt_GUIOBJN' defined but not used
+../fh_registry.h:433: warning: `fh_get_GUIOBJN' defined but not used
+../fh_registry.h:433: warning: `fh_kw_GUIOBJN' defined but not used
+../fh_registry.h:433: warning: `fh_cmt_GUIOBJN' defined but not used
+../fh_registry.h:434: warning: `fh_set_GUIMAGN' defined but not used
+../fh_registry.h:434: warning: `fh_setval_GUIMAGN' defined but not used
+../fh_registry.h:434: warning: `fh_setcmt_GUIMAGN' defined but not used
+../fh_registry.h:434: warning: `fh_get_GUIMAGN' defined but not used
+../fh_registry.h:434: warning: `fh_kw_GUIMAGN' defined but not used
+../fh_registry.h:434: warning: `fh_cmt_GUIMAGN' defined but not used
+../fh_registry.h:435: warning: `fh_set_XPROBE' defined but not used
+../fh_registry.h:435: warning: `fh_setval_XPROBE' defined but not used
+../fh_registry.h:435: warning: `fh_setcmt_XPROBE' defined but not used
+../fh_registry.h:435: warning: `fh_get_XPROBE' defined but not used
+../fh_registry.h:435: warning: `fh_kw_XPROBE' defined but not used
+../fh_registry.h:435: warning: `fh_cmt_XPROBE' defined but not used
+../fh_registry.h:436: warning: `fh_set_YPROBE' defined but not used
+../fh_registry.h:436: warning: `fh_setval_YPROBE' defined but not used
+../fh_registry.h:436: warning: `fh_setcmt_YPROBE' defined but not used
+../fh_registry.h:436: warning: `fh_get_YPROBE' defined but not used
+../fh_registry.h:436: warning: `fh_kw_YPROBE' defined but not used
+../fh_registry.h:436: warning: `fh_cmt_YPROBE' defined but not used
+../fh_registry.h:437: warning: `fh_set_ZPROBE' defined but not used
+../fh_registry.h:437: warning: `fh_setval_ZPROBE' defined but not used
+../fh_registry.h:437: warning: `fh_setcmt_ZPROBE' defined but not used
+../fh_registry.h:437: warning: `fh_get_ZPROBE' defined but not used
+../fh_registry.h:437: warning: `fh_kw_ZPROBE' defined but not used
+../fh_registry.h:437: warning: `fh_cmt_ZPROBE' defined but not used
+../fh_registry.h:438: warning: `fh_set_GUIFLUX' defined but not used
+../fh_registry.h:438: warning: `fh_setval_GUIFLUX' defined but not used
+../fh_registry.h:438: warning: `fh_setcmt_GUIFLUX' defined but not used
+../fh_registry.h:438: warning: `fh_get_GUIFLUX' defined but not used
+../fh_registry.h:438: warning: `fh_kw_GUIFLUX' defined but not used
+../fh_registry.h:438: warning: `fh_cmt_GUIFLUX' defined but not used
+../fh_registry.h:439: warning: `fh_set_GUIFWHX' defined but not used
+../fh_registry.h:439: warning: `fh_setval_GUIFWHX' defined but not used
+../fh_registry.h:439: warning: `fh_setcmt_GUIFWHX' defined but not used
+../fh_registry.h:439: warning: `fh_get_GUIFWHX' defined but not used
+../fh_registry.h:439: warning: `fh_kw_GUIFWHX' defined but not used
+../fh_registry.h:439: warning: `fh_cmt_GUIFWHX' defined but not used
+../fh_registry.h:440: warning: `fh_set_GUIFWHY' defined but not used
+../fh_registry.h:440: warning: `fh_setval_GUIFWHY' defined but not used
+../fh_registry.h:440: warning: `fh_setcmt_GUIFWHY' defined but not used
+../fh_registry.h:440: warning: `fh_get_GUIFWHY' defined but not used
+../fh_registry.h:440: warning: `fh_kw_GUIFWHY' defined but not used
+../fh_registry.h:440: warning: `fh_cmt_GUIFWHY' defined but not used
+../fh_registry.h:441: warning: `fh_set_SKYFLUX' defined but not used
+../fh_registry.h:441: warning: `fh_setval_SKYFLUX' defined but not used
+../fh_registry.h:441: warning: `fh_setcmt_SKYFLUX' defined but not used
+../fh_registry.h:441: warning: `fh_get_SKYFLUX' defined but not used
+../fh_registry.h:441: warning: `fh_kw_SKYFLUX' defined but not used
+../fh_registry.h:441: warning: `fh_cmt_SKYFLUX' defined but not used
+../fh_registry.h:443: warning: `fh_set_GUINAME1' defined but not used
+../fh_registry.h:443: warning: `fh_setval_GUINAME1' defined but not used
+../fh_registry.h:443: warning: `fh_setcmt_GUINAME1' defined but not used
+../fh_registry.h:443: warning: `fh_get_GUINAME1' defined but not used
+../fh_registry.h:443: warning: `fh_kw_GUINAME1' defined but not used
+../fh_registry.h:443: warning: `fh_cmt_GUINAME1' defined but not used
+../fh_registry.h:444: warning: `fh_set_GUIEQUI1' defined but not used
+../fh_registry.h:444: warning: `fh_setcmt_GUIEQUI1' defined but not used
+../fh_registry.h:444: warning: `fh_kw_GUIEQUI1' defined but not used
+../fh_registry.h:444: warning: `fh_cmt_GUIEQUI1' defined but not used
+../fh_registry.h:445: warning: `fh_set_GUIRADE1' defined but not used
+../fh_registry.h:445: warning: `fh_setval_GUIRADE1' defined but not used
+../fh_registry.h:445: warning: `fh_setcmt_GUIRADE1' defined but not used
+../fh_registry.h:445: warning: `fh_get_GUIRADE1' defined but not used
+../fh_registry.h:445: warning: `fh_kw_GUIRADE1' defined but not used
+../fh_registry.h:445: warning: `fh_cmt_GUIRADE1' defined but not used
+../fh_registry.h:446: warning: `fh_set_GUIRA1' defined but not used
+../fh_registry.h:446: warning: `fh_setval_GUIRA1' defined but not used
+../fh_registry.h:446: warning: `fh_setcmt_GUIRA1' defined but not used
+../fh_registry.h:446: warning: `fh_get_GUIRA1' defined but not used
+../fh_registry.h:446: warning: `fh_kw_GUIRA1' defined but not used
+../fh_registry.h:446: warning: `fh_cmt_GUIRA1' defined but not used
+../fh_registry.h:447: warning: `fh_set_GUIDEC1' defined but not used
+../fh_registry.h:447: warning: `fh_setval_GUIDEC1' defined but not used
+../fh_registry.h:447: warning: `fh_setcmt_GUIDEC1' defined but not used
+../fh_registry.h:447: warning: `fh_get_GUIDEC1' defined but not used
+../fh_registry.h:447: warning: `fh_kw_GUIDEC1' defined but not used
+../fh_registry.h:447: warning: `fh_cmt_GUIDEC1' defined but not used
+../fh_registry.h:448: warning: `fh_set_GUIRAPM1' defined but not used
+../fh_registry.h:448: warning: `fh_setval_GUIRAPM1' defined but not used
+../fh_registry.h:448: warning: `fh_setcmt_GUIRAPM1' defined but not used
+../fh_registry.h:448: warning: `fh_get_GUIRAPM1' defined but not used
+../fh_registry.h:448: warning: `fh_kw_GUIRAPM1' defined but not used
+../fh_registry.h:448: warning: `fh_cmt_GUIRAPM1' defined but not used
+../fh_registry.h:449: warning: `fh_set_GUIDEPM1' defined but not used
+../fh_registry.h:449: warning: `fh_setval_GUIDEPM1' defined but not used
+../fh_registry.h:449: warning: `fh_setcmt_GUIDEPM1' defined but not used
+../fh_registry.h:449: warning: `fh_get_GUIDEPM1' defined but not used
+../fh_registry.h:449: warning: `fh_kw_GUIDEPM1' defined but not used
+../fh_registry.h:449: warning: `fh_cmt_GUIDEPM1' defined but not used
+../fh_registry.h:450: warning: `fh_set_GUIOBJN1' defined but not used
+../fh_registry.h:450: warning: `fh_setval_GUIOBJN1' defined but not used
+../fh_registry.h:450: warning: `fh_setcmt_GUIOBJN1' defined but not used
+../fh_registry.h:450: warning: `fh_get_GUIOBJN1' defined but not used
+../fh_registry.h:450: warning: `fh_kw_GUIOBJN1' defined but not used
+../fh_registry.h:450: warning: `fh_cmt_GUIOBJN1' defined but not used
+../fh_registry.h:451: warning: `fh_set_GUIMAGN1' defined but not used
+../fh_registry.h:451: warning: `fh_setval_GUIMAGN1' defined but not used
+../fh_registry.h:451: warning: `fh_setcmt_GUIMAGN1' defined but not used
+../fh_registry.h:451: warning: `fh_get_GUIMAGN1' defined but not used
+../fh_registry.h:451: warning: `fh_kw_GUIMAGN1' defined but not used
+../fh_registry.h:451: warning: `fh_cmt_GUIMAGN1' defined but not used
+../fh_registry.h:452: warning: `fh_set_GUIPOSX1' defined but not used
+../fh_registry.h:452: warning: `fh_setval_GUIPOSX1' defined but not used
+../fh_registry.h:452: warning: `fh_setcmt_GUIPOSX1' defined but not used
+../fh_registry.h:452: warning: `fh_get_GUIPOSX1' defined but not used
+../fh_registry.h:452: warning: `fh_kw_GUIPOSX1' defined but not used
+../fh_registry.h:452: warning: `fh_cmt_GUIPOSX1' defined but not used
+../fh_registry.h:453: warning: `fh_set_GUIPOSY1' defined but not used
+../fh_registry.h:453: warning: `fh_setval_GUIPOSY1' defined but not used
+../fh_registry.h:453: warning: `fh_setcmt_GUIPOSY1' defined but not used
+../fh_registry.h:453: warning: `fh_get_GUIPOSY1' defined but not used
+../fh_registry.h:453: warning: `fh_kw_GUIPOSY1' defined but not used
+../fh_registry.h:453: warning: `fh_cmt_GUIPOSY1' defined but not used
+../fh_registry.h:454: warning: `fh_set_GUIPOSZ1' defined but not used
+../fh_registry.h:454: warning: `fh_setval_GUIPOSZ1' defined but not used
+../fh_registry.h:454: warning: `fh_setcmt_GUIPOSZ1' defined but not used
+../fh_registry.h:454: warning: `fh_get_GUIPOSZ1' defined but not used
+../fh_registry.h:454: warning: `fh_kw_GUIPOSZ1' defined but not used
+../fh_registry.h:454: warning: `fh_cmt_GUIPOSZ1' defined but not used
+../fh_registry.h:455: warning: `fh_set_GUIFLUX1' defined but not used
+../fh_registry.h:455: warning: `fh_setval_GUIFLUX1' defined but not used
+../fh_registry.h:455: warning: `fh_setcmt_GUIFLUX1' defined but not used
+../fh_registry.h:455: warning: `fh_get_GUIFLUX1' defined but not used
+../fh_registry.h:455: warning: `fh_kw_GUIFLUX1' defined but not used
+../fh_registry.h:455: warning: `fh_cmt_GUIFLUX1' defined but not used
+../fh_registry.h:456: warning: `fh_set_GUIFWHX1' defined but not used
+../fh_registry.h:456: warning: `fh_setval_GUIFWHX1' defined but not used
+../fh_registry.h:456: warning: `fh_setcmt_GUIFWHX1' defined but not used
+../fh_registry.h:456: warning: `fh_get_GUIFWHX1' defined but not used
+../fh_registry.h:456: warning: `fh_kw_GUIFWHX1' defined but not used
+../fh_registry.h:456: warning: `fh_cmt_GUIFWHX1' defined but not used
+../fh_registry.h:457: warning: `fh_set_GUIFWHY1' defined but not used
+../fh_registry.h:457: warning: `fh_setval_GUIFWHY1' defined but not used
+../fh_registry.h:457: warning: `fh_setcmt_GUIFWHY1' defined but not used
+../fh_registry.h:457: warning: `fh_get_GUIFWHY1' defined but not used
+../fh_registry.h:457: warning: `fh_kw_GUIFWHY1' defined but not used
+../fh_registry.h:457: warning: `fh_cmt_GUIFWHY1' defined but not used
+../fh_registry.h:459: warning: `fh_set_GUINAME2' defined but not used
+../fh_registry.h:459: warning: `fh_setval_GUINAME2' defined but not used
+../fh_registry.h:459: warning: `fh_setcmt_GUINAME2' defined but not used
+../fh_registry.h:459: warning: `fh_get_GUINAME2' defined but not used
+../fh_registry.h:459: warning: `fh_kw_GUINAME2' defined but not used
+../fh_registry.h:459: warning: `fh_cmt_GUINAME2' defined but not used
+../fh_registry.h:460: warning: `fh_set_GUIEQUI2' defined but not used
+../fh_registry.h:460: warning: `fh_setcmt_GUIEQUI2' defined but not used
+../fh_registry.h:460: warning: `fh_kw_GUIEQUI2' defined but not used
+../fh_registry.h:460: warning: `fh_cmt_GUIEQUI2' defined but not used
+../fh_registry.h:461: warning: `fh_set_GUIRADE2' defined but not used
+../fh_registry.h:461: warning: `fh_setval_GUIRADE2' defined but not used
+../fh_registry.h:461: warning: `fh_setcmt_GUIRADE2' defined but not used
+../fh_registry.h:461: warning: `fh_get_GUIRADE2' defined but not used
+../fh_registry.h:461: warning: `fh_kw_GUIRADE2' defined but not used
+../fh_registry.h:461: warning: `fh_cmt_GUIRADE2' defined but not used
+../fh_registry.h:462: warning: `fh_set_GUIRA2' defined but not used
+../fh_registry.h:462: warning: `fh_setval_GUIRA2' defined but not used
+../fh_registry.h:462: warning: `fh_setcmt_GUIRA2' defined but not used
+../fh_registry.h:462: warning: `fh_get_GUIRA2' defined but not used
+../fh_registry.h:462: warning: `fh_kw_GUIRA2' defined but not used
+../fh_registry.h:462: warning: `fh_cmt_GUIRA2' defined but not used
+../fh_registry.h:463: warning: `fh_set_GUIDEC2' defined but not used
+../fh_registry.h:463: warning: `fh_setval_GUIDEC2' defined but not used
+../fh_registry.h:463: warning: `fh_setcmt_GUIDEC2' defined but not used
+../fh_registry.h:463: warning: `fh_get_GUIDEC2' defined but not used
+../fh_registry.h:463: warning: `fh_kw_GUIDEC2' defined but not used
+../fh_registry.h:463: warning: `fh_cmt_GUIDEC2' defined but not used
+../fh_registry.h:464: warning: `fh_set_GUIRAPM2' defined but not used
+../fh_registry.h:464: warning: `fh_setval_GUIRAPM2' defined but not used
+../fh_registry.h:464: warning: `fh_setcmt_GUIRAPM2' defined but not used
+../fh_registry.h:464: warning: `fh_get_GUIRAPM2' defined but not used
+../fh_registry.h:464: warning: `fh_kw_GUIRAPM2' defined but not used
+../fh_registry.h:464: warning: `fh_cmt_GUIRAPM2' defined but not used
+../fh_registry.h:465: warning: `fh_set_GUIDEPM2' defined but not used
+../fh_registry.h:465: warning: `fh_setval_GUIDEPM2' defined but not used
+../fh_registry.h:465: warning: `fh_setcmt_GUIDEPM2' defined but not used
+../fh_registry.h:465: warning: `fh_get_GUIDEPM2' defined but not used
+../fh_registry.h:465: warning: `fh_kw_GUIDEPM2' defined but not used
+../fh_registry.h:465: warning: `fh_cmt_GUIDEPM2' defined but not used
+../fh_registry.h:466: warning: `fh_set_GUIOBJN2' defined but not used
+../fh_registry.h:466: warning: `fh_setval_GUIOBJN2' defined but not used
+../fh_registry.h:466: warning: `fh_setcmt_GUIOBJN2' defined but not used
+../fh_registry.h:466: warning: `fh_get_GUIOBJN2' defined but not used
+../fh_registry.h:466: warning: `fh_kw_GUIOBJN2' defined but not used
+../fh_registry.h:466: warning: `fh_cmt_GUIOBJN2' defined but not used
+../fh_registry.h:467: warning: `fh_set_GUIMAGN2' defined but not used
+../fh_registry.h:467: warning: `fh_setval_GUIMAGN2' defined but not used
+../fh_registry.h:467: warning: `fh_setcmt_GUIMAGN2' defined but not used
+../fh_registry.h:467: warning: `fh_get_GUIMAGN2' defined but not used
+../fh_registry.h:467: warning: `fh_kw_GUIMAGN2' defined but not used
+../fh_registry.h:467: warning: `fh_cmt_GUIMAGN2' defined but not used
+../fh_registry.h:468: warning: `fh_set_GUIPOSX2' defined but not used
+../fh_registry.h:468: warning: `fh_setval_GUIPOSX2' defined but not used
+../fh_registry.h:468: warning: `fh_setcmt_GUIPOSX2' defined but not used
+../fh_registry.h:468: warning: `fh_get_GUIPOSX2' defined but not used
+../fh_registry.h:468: warning: `fh_kw_GUIPOSX2' defined but not used
+../fh_registry.h:468: warning: `fh_cmt_GUIPOSX2' defined but not used
+../fh_registry.h:469: warning: `fh_set_GUIPOSY2' defined but not used
+../fh_registry.h:469: warning: `fh_setval_GUIPOSY2' defined but not used
+../fh_registry.h:469: warning: `fh_setcmt_GUIPOSY2' defined but not used
+../fh_registry.h:469: warning: `fh_get_GUIPOSY2' defined but not used
+../fh_registry.h:469: warning: `fh_kw_GUIPOSY2' defined but not used
+../fh_registry.h:469: warning: `fh_cmt_GUIPOSY2' defined but not used
+../fh_registry.h:470: warning: `fh_set_GUIPOSZ2' defined but not used
+../fh_registry.h:470: warning: `fh_setval_GUIPOSZ2' defined but not used
+../fh_registry.h:470: warning: `fh_setcmt_GUIPOSZ2' defined but not used
+../fh_registry.h:470: warning: `fh_get_GUIPOSZ2' defined but not used
+../fh_registry.h:470: warning: `fh_kw_GUIPOSZ2' defined but not used
+../fh_registry.h:470: warning: `fh_cmt_GUIPOSZ2' defined but not used
+../fh_registry.h:471: warning: `fh_set_GUIFLUX2' defined but not used
+../fh_registry.h:471: warning: `fh_setval_GUIFLUX2' defined but not used
+../fh_registry.h:471: warning: `fh_setcmt_GUIFLUX2' defined but not used
+../fh_registry.h:471: warning: `fh_get_GUIFLUX2' defined but not used
+../fh_registry.h:471: warning: `fh_kw_GUIFLUX2' defined but not used
+../fh_registry.h:471: warning: `fh_cmt_GUIFLUX2' defined but not used
+../fh_registry.h:472: warning: `fh_set_GUIFWHX2' defined but not used
+../fh_registry.h:472: warning: `fh_setval_GUIFWHX2' defined but not used
+../fh_registry.h:472: warning: `fh_setcmt_GUIFWHX2' defined but not used
+../fh_registry.h:472: warning: `fh_get_GUIFWHX2' defined but not used
+../fh_registry.h:472: warning: `fh_kw_GUIFWHX2' defined but not used
+../fh_registry.h:472: warning: `fh_cmt_GUIFWHX2' defined but not used
+../fh_registry.h:473: warning: `fh_set_GUIFWHY2' defined but not used
+../fh_registry.h:473: warning: `fh_setval_GUIFWHY2' defined but not used
+../fh_registry.h:473: warning: `fh_setcmt_GUIFWHY2' defined but not used
+../fh_registry.h:473: warning: `fh_get_GUIFWHY2' defined but not used
+../fh_registry.h:473: warning: `fh_kw_GUIFWHY2' defined but not used
+../fh_registry.h:473: warning: `fh_cmt_GUIFWHY2' defined but not used
+../fh_registry.h:475: warning: `fh_set_GUINAME3' defined but not used
+../fh_registry.h:475: warning: `fh_setval_GUINAME3' defined but not used
+../fh_registry.h:475: warning: `fh_setcmt_GUINAME3' defined but not used
+../fh_registry.h:475: warning: `fh_get_GUINAME3' defined but not used
+../fh_registry.h:475: warning: `fh_kw_GUINAME3' defined but not used
+../fh_registry.h:475: warning: `fh_cmt_GUINAME3' defined but not used
+../fh_registry.h:476: warning: `fh_set_GUIEQUI3' defined but not used
+../fh_registry.h:476: warning: `fh_setcmt_GUIEQUI3' defined but not used
+../fh_registry.h:476: warning: `fh_kw_GUIEQUI3' defined but not used
+../fh_registry.h:476: warning: `fh_cmt_GUIEQUI3' defined but not used
+../fh_registry.h:477: warning: `fh_set_GUIRADE3' defined but not used
+../fh_registry.h:477: warning: `fh_setval_GUIRADE3' defined but not used
+../fh_registry.h:477: warning: `fh_setcmt_GUIRADE3' defined but not used
+../fh_registry.h:477: warning: `fh_get_GUIRADE3' defined but not used
+../fh_registry.h:477: warning: `fh_kw_GUIRADE3' defined but not used
+../fh_registry.h:477: warning: `fh_cmt_GUIRADE3' defined but not used
+../fh_registry.h:478: warning: `fh_set_GUIRA3' defined but not used
+../fh_registry.h:478: warning: `fh_setval_GUIRA3' defined but not used
+../fh_registry.h:478: warning: `fh_setcmt_GUIRA3' defined but not used
+../fh_registry.h:478: warning: `fh_get_GUIRA3' defined but not used
+../fh_registry.h:478: warning: `fh_kw_GUIRA3' defined but not used
+../fh_registry.h:478: warning: `fh_cmt_GUIRA3' defined but not used
+../fh_registry.h:479: warning: `fh_set_GUIDEC3' defined but not used
+../fh_registry.h:479: warning: `fh_setval_GUIDEC3' defined but not used
+../fh_registry.h:479: warning: `fh_setcmt_GUIDEC3' defined but not used
+../fh_registry.h:479: warning: `fh_get_GUIDEC3' defined but not used
+../fh_registry.h:479: warning: `fh_kw_GUIDEC3' defined but not used
+../fh_registry.h:479: warning: `fh_cmt_GUIDEC3' defined but not used
+../fh_registry.h:480: warning: `fh_set_GUIRAPM3' defined but not used
+../fh_registry.h:480: warning: `fh_setval_GUIRAPM3' defined but not used
+../fh_registry.h:480: warning: `fh_setcmt_GUIRAPM3' defined but not used
+../fh_registry.h:480: warning: `fh_get_GUIRAPM3' defined but not used
+../fh_registry.h:480: warning: `fh_kw_GUIRAPM3' defined but not used
+../fh_registry.h:480: warning: `fh_cmt_GUIRAPM3' defined but not used
+../fh_registry.h:481: warning: `fh_set_GUIDEPM3' defined but not used
+../fh_registry.h:481: warning: `fh_setval_GUIDEPM3' defined but not used
+../fh_registry.h:481: warning: `fh_setcmt_GUIDEPM3' defined but not used
+../fh_registry.h:481: warning: `fh_get_GUIDEPM3' defined but not used
+../fh_registry.h:481: warning: `fh_kw_GUIDEPM3' defined but not used
+../fh_registry.h:481: warning: `fh_cmt_GUIDEPM3' defined but not used
+../fh_registry.h:482: warning: `fh_set_GUIOBJN3' defined but not used
+../fh_registry.h:482: warning: `fh_setval_GUIOBJN3' defined but not used
+../fh_registry.h:482: warning: `fh_setcmt_GUIOBJN3' defined but not used
+../fh_registry.h:482: warning: `fh_get_GUIOBJN3' defined but not used
+../fh_registry.h:482: warning: `fh_kw_GUIOBJN3' defined but not used
+../fh_registry.h:482: warning: `fh_cmt_GUIOBJN3' defined but not used
+../fh_registry.h:483: warning: `fh_set_GUIMAGN3' defined but not used
+../fh_registry.h:483: warning: `fh_setval_GUIMAGN3' defined but not used
+../fh_registry.h:483: warning: `fh_setcmt_GUIMAGN3' defined but not used
+../fh_registry.h:483: warning: `fh_get_GUIMAGN3' defined but not used
+../fh_registry.h:483: warning: `fh_kw_GUIMAGN3' defined but not used
+../fh_registry.h:483: warning: `fh_cmt_GUIMAGN3' defined but not used
+../fh_registry.h:484: warning: `fh_set_GUIPOSX3' defined but not used
+../fh_registry.h:484: warning: `fh_setval_GUIPOSX3' defined but not used
+../fh_registry.h:484: warning: `fh_setcmt_GUIPOSX3' defined but not used
+../fh_registry.h:484: warning: `fh_get_GUIPOSX3' defined but not used
+../fh_registry.h:484: warning: `fh_kw_GUIPOSX3' defined but not used
+../fh_registry.h:484: warning: `fh_cmt_GUIPOSX3' defined but not used
+../fh_registry.h:485: warning: `fh_set_GUIPOSY3' defined but not used
+../fh_registry.h:485: warning: `fh_setval_GUIPOSY3' defined but not used
+../fh_registry.h:485: warning: `fh_setcmt_GUIPOSY3' defined but not used
+../fh_registry.h:485: warning: `fh_get_GUIPOSY3' defined but not used
+../fh_registry.h:485: warning: `fh_kw_GUIPOSY3' defined but not used
+../fh_registry.h:485: warning: `fh_cmt_GUIPOSY3' defined but not used
+../fh_registry.h:486: warning: `fh_set_GUIPOSZ3' defined but not used
+../fh_registry.h:486: warning: `fh_setval_GUIPOSZ3' defined but not used
+../fh_registry.h:486: warning: `fh_setcmt_GUIPOSZ3' defined but not used
+../fh_registry.h:486: warning: `fh_get_GUIPOSZ3' defined but not used
+../fh_registry.h:486: warning: `fh_kw_GUIPOSZ3' defined but not used
+../fh_registry.h:486: warning: `fh_cmt_GUIPOSZ3' defined but not used
+../fh_registry.h:487: warning: `fh_set_GUIFLUX3' defined but not used
+../fh_registry.h:487: warning: `fh_setval_GUIFLUX3' defined but not used
+../fh_registry.h:487: warning: `fh_setcmt_GUIFLUX3' defined but not used
+../fh_registry.h:487: warning: `fh_get_GUIFLUX3' defined but not used
+../fh_registry.h:487: warning: `fh_kw_GUIFLUX3' defined but not used
+../fh_registry.h:487: warning: `fh_cmt_GUIFLUX3' defined but not used
+../fh_registry.h:488: warning: `fh_set_GUIFWHX3' defined but not used
+../fh_registry.h:488: warning: `fh_setval_GUIFWHX3' defined but not used
+../fh_registry.h:488: warning: `fh_setcmt_GUIFWHX3' defined but not used
+../fh_registry.h:488: warning: `fh_get_GUIFWHX3' defined but not used
+../fh_registry.h:488: warning: `fh_kw_GUIFWHX3' defined but not used
+../fh_registry.h:488: warning: `fh_cmt_GUIFWHX3' defined but not used
+../fh_registry.h:489: warning: `fh_set_GUIFWHY3' defined but not used
+../fh_registry.h:489: warning: `fh_setval_GUIFWHY3' defined but not used
+../fh_registry.h:489: warning: `fh_setcmt_GUIFWHY3' defined but not used
+../fh_registry.h:489: warning: `fh_get_GUIFWHY3' defined but not used
+../fh_registry.h:489: warning: `fh_kw_GUIFWHY3' defined but not used
+../fh_registry.h:489: warning: `fh_cmt_GUIFWHY3' defined but not used
+../fh_registry.h:491: warning: `fh_set_GUINAME4' defined but not used
+../fh_registry.h:491: warning: `fh_setval_GUINAME4' defined but not used
+../fh_registry.h:491: warning: `fh_setcmt_GUINAME4' defined but not used
+../fh_registry.h:491: warning: `fh_get_GUINAME4' defined but not used
+../fh_registry.h:491: warning: `fh_kw_GUINAME4' defined but not used
+../fh_registry.h:491: warning: `fh_cmt_GUINAME4' defined but not used
+../fh_registry.h:492: warning: `fh_set_GUIEQUI4' defined but not used
+../fh_registry.h:492: warning: `fh_setcmt_GUIEQUI4' defined but not used
+../fh_registry.h:492: warning: `fh_kw_GUIEQUI4' defined but not used
+../fh_registry.h:492: warning: `fh_cmt_GUIEQUI4' defined but not used
+../fh_registry.h:493: warning: `fh_set_GUIRADE4' defined but not used
+../fh_registry.h:493: warning: `fh_setval_GUIRADE4' defined but not used
+../fh_registry.h:493: warning: `fh_setcmt_GUIRADE4' defined but not used
+../fh_registry.h:493: warning: `fh_get_GUIRADE4' defined but not used
+../fh_registry.h:493: warning: `fh_kw_GUIRADE4' defined but not used
+../fh_registry.h:493: warning: `fh_cmt_GUIRADE4' defined but not used
+../fh_registry.h:494: warning: `fh_set_GUIRA4' defined but not used
+../fh_registry.h:494: warning: `fh_setval_GUIRA4' defined but not used
+../fh_registry.h:494: warning: `fh_setcmt_GUIRA4' defined but not used
+../fh_registry.h:494: warning: `fh_get_GUIRA4' defined but not used
+../fh_registry.h:494: warning: `fh_kw_GUIRA4' defined but not used
+../fh_registry.h:494: warning: `fh_cmt_GUIRA4' defined but not used
+../fh_registry.h:495: warning: `fh_set_GUIDEC4' defined but not used
+../fh_registry.h:495: warning: `fh_setval_GUIDEC4' defined but not used
+../fh_registry.h:495: warning: `fh_setcmt_GUIDEC4' defined but not used
+../fh_registry.h:495: warning: `fh_get_GUIDEC4' defined but not used
+../fh_registry.h:495: warning: `fh_kw_GUIDEC4' defined but not used
+../fh_registry.h:495: warning: `fh_cmt_GUIDEC4' defined but not used
+../fh_registry.h:496: warning: `fh_set_GUIRAPM4' defined but not used
+../fh_registry.h:496: warning: `fh_setval_GUIRAPM4' defined but not used
+../fh_registry.h:496: warning: `fh_setcmt_GUIRAPM4' defined but not used
+../fh_registry.h:496: warning: `fh_get_GUIRAPM4' defined but not used
+../fh_registry.h:496: warning: `fh_kw_GUIRAPM4' defined but not used
+../fh_registry.h:496: warning: `fh_cmt_GUIRAPM4' defined but not used
+../fh_registry.h:497: warning: `fh_set_GUIDEPM4' defined but not used
+../fh_registry.h:497: warning: `fh_setval_GUIDEPM4' defined but not used
+../fh_registry.h:497: warning: `fh_setcmt_GUIDEPM4' defined but not used
+../fh_registry.h:497: warning: `fh_get_GUIDEPM4' defined but not used
+../fh_registry.h:497: warning: `fh_kw_GUIDEPM4' defined but not used
+../fh_registry.h:497: warning: `fh_cmt_GUIDEPM4' defined but not used
+../fh_registry.h:498: warning: `fh_set_GUIOBJN4' defined but not used
+../fh_registry.h:498: warning: `fh_setval_GUIOBJN4' defined but not used
+../fh_registry.h:498: warning: `fh_setcmt_GUIOBJN4' defined but not used
+../fh_registry.h:498: warning: `fh_get_GUIOBJN4' defined but not used
+../fh_registry.h:498: warning: `fh_kw_GUIOBJN4' defined but not used
+../fh_registry.h:498: warning: `fh_cmt_GUIOBJN4' defined but not used
+../fh_registry.h:499: warning: `fh_set_GUIMAGN4' defined but not used
+../fh_registry.h:499: warning: `fh_setval_GUIMAGN4' defined but not used
+../fh_registry.h:499: warning: `fh_setcmt_GUIMAGN4' defined but not used
+../fh_registry.h:499: warning: `fh_get_GUIMAGN4' defined but not used
+../fh_registry.h:499: warning: `fh_kw_GUIMAGN4' defined but not used
+../fh_registry.h:499: warning: `fh_cmt_GUIMAGN4' defined but not used
+../fh_registry.h:500: warning: `fh_set_GUIPOSX4' defined but not used
+../fh_registry.h:500: warning: `fh_setval_GUIPOSX4' defined but not used
+../fh_registry.h:500: warning: `fh_setcmt_GUIPOSX4' defined but not used
+../fh_registry.h:500: warning: `fh_get_GUIPOSX4' defined but not used
+../fh_registry.h:500: warning: `fh_kw_GUIPOSX4' defined but not used
+../fh_registry.h:500: warning: `fh_cmt_GUIPOSX4' defined but not used
+../fh_registry.h:501: warning: `fh_set_GUIPOSY4' defined but not used
+../fh_registry.h:501: warning: `fh_setval_GUIPOSY4' defined but not used
+../fh_registry.h:501: warning: `fh_setcmt_GUIPOSY4' defined but not used
+../fh_registry.h:501: warning: `fh_get_GUIPOSY4' defined but not used
+../fh_registry.h:501: warning: `fh_kw_GUIPOSY4' defined but not used
+../fh_registry.h:501: warning: `fh_cmt_GUIPOSY4' defined but not used
+../fh_registry.h:502: warning: `fh_set_GUIPOSZ4' defined but not used
+../fh_registry.h:502: warning: `fh_setval_GUIPOSZ4' defined but not used
+../fh_registry.h:502: warning: `fh_setcmt_GUIPOSZ4' defined but not used
+../fh_registry.h:502: warning: `fh_get_GUIPOSZ4' defined but not used
+../fh_registry.h:502: warning: `fh_kw_GUIPOSZ4' defined but not used
+../fh_registry.h:502: warning: `fh_cmt_GUIPOSZ4' defined but not used
+../fh_registry.h:503: warning: `fh_set_GUIFLUX4' defined but not used
+../fh_registry.h:503: warning: `fh_setval_GUIFLUX4' defined but not used
+../fh_registry.h:503: warning: `fh_setcmt_GUIFLUX4' defined but not used
+../fh_registry.h:503: warning: `fh_get_GUIFLUX4' defined but not used
+../fh_registry.h:503: warning: `fh_kw_GUIFLUX4' defined but not used
+../fh_registry.h:503: warning: `fh_cmt_GUIFLUX4' defined but not used
+../fh_registry.h:504: warning: `fh_set_GUIFWHX4' defined but not used
+../fh_registry.h:504: warning: `fh_setval_GUIFWHX4' defined but not used
+../fh_registry.h:504: warning: `fh_setcmt_GUIFWHX4' defined but not used
+../fh_registry.h:504: warning: `fh_get_GUIFWHX4' defined but not used
+../fh_registry.h:504: warning: `fh_kw_GUIFWHX4' defined but not used
+../fh_registry.h:504: warning: `fh_cmt_GUIFWHX4' defined but not used
+../fh_registry.h:505: warning: `fh_set_GUIFWHY4' defined but not used
+../fh_registry.h:505: warning: `fh_setval_GUIFWHY4' defined but not used
+../fh_registry.h:505: warning: `fh_setcmt_GUIFWHY4' defined but not used
+../fh_registry.h:505: warning: `fh_get_GUIFWHY4' defined but not used
+../fh_registry.h:505: warning: `fh_kw_GUIFWHY4' defined but not used
+../fh_registry.h:505: warning: `fh_cmt_GUIFWHY4' defined but not used
+../fh_registry.h:507: warning: `fh_set_AIRMASS' defined but not used
+../fh_registry.h:507: warning: `fh_setval_AIRMASS' defined but not used
+../fh_registry.h:507: warning: `fh_setcmt_AIRMASS' defined but not used
+../fh_registry.h:507: warning: `fh_get_AIRMASS' defined but not used
+../fh_registry.h:507: warning: `fh_kw_AIRMASS' defined but not used
+../fh_registry.h:507: warning: `fh_cmt_AIRMASS' defined but not used
+../fh_registry.h:508: warning: `fh_set_TELALT' defined but not used
+../fh_registry.h:508: warning: `fh_setval_TELALT' defined but not used
+../fh_registry.h:508: warning: `fh_setcmt_TELALT' defined but not used
+../fh_registry.h:508: warning: `fh_get_TELALT' defined but not used
+../fh_registry.h:508: warning: `fh_kw_TELALT' defined but not used
+../fh_registry.h:508: warning: `fh_cmt_TELALT' defined but not used
+../fh_registry.h:509: warning: `fh_set_TELAZ' defined but not used
+../fh_registry.h:509: warning: `fh_setval_TELAZ' defined but not used
+../fh_registry.h:509: warning: `fh_setcmt_TELAZ' defined but not used
+../fh_registry.h:509: warning: `fh_get_TELAZ' defined but not used
+../fh_registry.h:509: warning: `fh_kw_TELAZ' defined but not used
+../fh_registry.h:509: warning: `fh_cmt_TELAZ' defined but not used
+../fh_registry.h:510: warning: `fh_set_MOONANGL' defined but not used
+../fh_registry.h:510: warning: `fh_setval_MOONANGL' defined but not used
+../fh_registry.h:510: warning: `fh_setcmt_MOONANGL' defined but not used
+../fh_registry.h:510: warning: `fh_get_MOONANGL' defined but not used
+../fh_registry.h:510: warning: `fh_kw_MOONANGL' defined but not used
+../fh_registry.h:510: warning: `fh_cmt_MOONANGL' defined but not used
+../fh_registry.h:511: warning: `fh_set_FOCUSID' defined but not used
+../fh_registry.h:511: warning: `fh_setval_FOCUSID' defined but not used
+../fh_registry.h:511: warning: `fh_setcmt_FOCUSID' defined but not used
+../fh_registry.h:511: warning: `fh_get_FOCUSID' defined but not used
+../fh_registry.h:511: warning: `fh_kw_FOCUSID' defined but not used
+../fh_registry.h:511: warning: `fh_cmt_FOCUSID' defined but not used
+../fh_registry.h:512: warning: `fh_set_TELCONF' defined but not used
+../fh_registry.h:512: warning: `fh_setval_TELCONF' defined but not used
+../fh_registry.h:512: warning: `fh_setcmt_TELCONF' defined but not used
+../fh_registry.h:512: warning: `fh_get_TELCONF' defined but not used
+../fh_registry.h:512: warning: `fh_kw_TELCONF' defined but not used
+../fh_registry.h:512: warning: `fh_cmt_TELCONF' defined but not used
+../fh_registry.h:513: warning: `fh_set_FOCUSPOS' defined but not used
+../fh_registry.h:513: warning: `fh_setval_FOCUSPOS' defined but not used
+../fh_registry.h:513: warning: `fh_setcmt_FOCUSPOS' defined but not used
+../fh_registry.h:513: warning: `fh_get_FOCUSPOS' defined but not used
+../fh_registry.h:513: warning: `fh_kw_FOCUSPOS' defined but not used
+../fh_registry.h:513: warning: `fh_cmt_FOCUSPOS' defined but not used
+../fh_registry.h:514: warning: `fh_set_TELFOCUS' defined but not used
+../fh_registry.h:514: warning: `fh_setval_TELFOCUS' defined but not used
+../fh_registry.h:514: warning: `fh_setcmt_TELFOCUS' defined but not used
+../fh_registry.h:514: warning: `fh_get_TELFOCUS' defined but not used
+../fh_registry.h:514: warning: `fh_kw_TELFOCUS' defined but not used
+../fh_registry.h:514: warning: `fh_cmt_TELFOCUS' defined but not used
+../fh_registry.h:515: warning: `fh_set_BONANGLE' defined but not used
+../fh_registry.h:515: warning: `fh_setval_BONANGLE' defined but not used
+../fh_registry.h:515: warning: `fh_setcmt_BONANGLE' defined but not used
+../fh_registry.h:515: warning: `fh_get_BONANGLE' defined but not used
+../fh_registry.h:515: warning: `fh_kw_BONANGLE' defined but not used
+../fh_registry.h:515: warning: `fh_cmt_BONANGLE' defined but not used
+../fh_registry.h:516: warning: `fh_set_ROTANGLE' defined but not used
+../fh_registry.h:516: warning: `fh_setval_ROTANGLE' defined but not used
+../fh_registry.h:516: warning: `fh_setcmt_ROTANGLE' defined but not used
+../fh_registry.h:516: warning: `fh_get_ROTANGLE' defined but not used
+../fh_registry.h:516: warning: `fh_kw_ROTANGLE' defined but not used
+../fh_registry.h:516: warning: `fh_cmt_ROTANGLE' defined but not used
+../fh_registry.h:524: warning: `fh_set_ISUGAIN' defined but not used
+../fh_registry.h:524: warning: `fh_setval_ISUGAIN' defined but not used
+../fh_registry.h:524: warning: `fh_setcmt_ISUGAIN' defined but not used
+../fh_registry.h:524: warning: `fh_get_ISUGAIN' defined but not used
+../fh_registry.h:524: warning: `fh_kw_ISUGAIN' defined but not used
+../fh_registry.h:524: warning: `fh_cmt_ISUGAIN' defined but not used
+../fh_registry.h:525: warning: `fh_set_ISURATE' defined but not used
+../fh_registry.h:525: warning: `fh_setval_ISURATE' defined but not used
+../fh_registry.h:525: warning: `fh_setcmt_ISURATE' defined but not used
+../fh_registry.h:525: warning: `fh_get_ISURATE' defined but not used
+../fh_registry.h:525: warning: `fh_kw_ISURATE' defined but not used
+../fh_registry.h:525: warning: `fh_cmt_ISURATE' defined but not used
+../fh_registry.h:526: warning: `fh_set_ISUTCSOR' defined but not used
+../fh_registry.h:526: warning: `fh_setval_ISUTCSOR' defined but not used
+../fh_registry.h:526: warning: `fh_setcmt_ISUTCSOR' defined but not used
+../fh_registry.h:526: warning: `fh_get_ISUTCSOR' defined but not used
+../fh_registry.h:526: warning: `fh_kw_ISUTCSOR' defined but not used
+../fh_registry.h:526: warning: `fh_cmt_ISUTCSOR' defined but not used
+../fh_registry.h:527: warning: `fh_set_ISUSTATE' defined but not used
+../fh_registry.h:527: warning: `fh_setval_ISUSTATE' defined but not used
+../fh_registry.h:527: warning: `fh_setcmt_ISUSTATE' defined but not used
+../fh_registry.h:527: warning: `fh_get_ISUSTATE' defined but not used
+../fh_registry.h:527: warning: `fh_kw_ISUSTATE' defined but not used
+../fh_registry.h:527: warning: `fh_cmt_ISUSTATE' defined but not used
+../fh_registry.h:528: warning: `fh_set_ISUSTDVX' defined but not used
+../fh_registry.h:528: warning: `fh_setval_ISUSTDVX' defined but not used
+../fh_registry.h:528: warning: `fh_setcmt_ISUSTDVX' defined but not used
+../fh_registry.h:528: warning: `fh_get_ISUSTDVX' defined but not used
+../fh_registry.h:528: warning: `fh_kw_ISUSTDVX' defined but not used
+../fh_registry.h:528: warning: `fh_cmt_ISUSTDVX' defined but not used
+../fh_registry.h:529: warning: `fh_set_ISUSTDVY' defined but not used
+../fh_registry.h:529: warning: `fh_setval_ISUSTDVY' defined but not used
+../fh_registry.h:529: warning: `fh_setcmt_ISUSTDVY' defined but not used
+../fh_registry.h:529: warning: `fh_get_ISUSTDVY' defined but not used
+../fh_registry.h:529: warning: `fh_kw_ISUSTDVY' defined but not used
+../fh_registry.h:529: warning: `fh_cmt_ISUSTDVY' defined but not used
+../fh_registry.h:537: warning: `fh_set_FSAGAIN' defined but not used
+../fh_registry.h:537: warning: `fh_setval_FSAGAIN' defined but not used
+../fh_registry.h:537: warning: `fh_setcmt_FSAGAIN' defined but not used
+../fh_registry.h:537: warning: `fh_get_FSAGAIN' defined but not used
+../fh_registry.h:537: warning: `fh_kw_FSAGAIN' defined but not used
+../fh_registry.h:537: warning: `fh_cmt_FSAGAIN' defined but not used
+../fh_registry.h:538: warning: `fh_set_FSARATE' defined but not used
+../fh_registry.h:538: warning: `fh_setval_FSARATE' defined but not used
+../fh_registry.h:538: warning: `fh_setcmt_FSARATE' defined but not used
+../fh_registry.h:538: warning: `fh_get_FSARATE' defined but not used
+../fh_registry.h:538: warning: `fh_kw_FSARATE' defined but not used
+../fh_registry.h:538: warning: `fh_cmt_FSARATE' defined but not used
+../fh_registry.h:539: warning: `fh_set_FSATHRES' defined but not used
+../fh_registry.h:539: warning: `fh_setval_FSATHRES' defined but not used
+../fh_registry.h:539: warning: `fh_setcmt_FSATHRES' defined but not used
+../fh_registry.h:539: warning: `fh_get_FSATHRES' defined but not used
+../fh_registry.h:539: warning: `fh_kw_FSATHRES' defined but not used
+../fh_registry.h:539: warning: `fh_cmt_FSATHRES' defined but not used
+../fh_registry.h:540: warning: `fh_set_FSASTATE' defined but not used
+../fh_registry.h:540: warning: `fh_setval_FSASTATE' defined but not used
+../fh_registry.h:540: warning: `fh_setcmt_FSASTATE' defined but not used
+../fh_registry.h:540: warning: `fh_get_FSASTATE' defined but not used
+../fh_registry.h:540: warning: `fh_kw_FSASTATE' defined but not used
+../fh_registry.h:540: warning: `fh_cmt_FSASTATE' defined but not used
+../fh_registry.h:541: warning: `fh_set_FSANMOVE' defined but not used
+../fh_registry.h:541: warning: `fh_setval_FSANMOVE' defined but not used
+../fh_registry.h:541: warning: `fh_setcmt_FSANMOVE' defined but not used
+../fh_registry.h:541: warning: `fh_get_FSANMOVE' defined but not used
+../fh_registry.h:541: warning: `fh_kw_FSANMOVE' defined but not used
+../fh_registry.h:541: warning: `fh_cmt_FSANMOVE' defined but not used
+../fh_registry.h:542: warning: `fh_set_FSAMINZ' defined but not used
+../fh_registry.h:542: warning: `fh_setval_FSAMINZ' defined but not used
+../fh_registry.h:542: warning: `fh_setcmt_FSAMINZ' defined but not used
+../fh_registry.h:542: warning: `fh_get_FSAMINZ' defined but not used
+../fh_registry.h:542: warning: `fh_kw_FSAMINZ' defined but not used
+../fh_registry.h:542: warning: `fh_cmt_FSAMINZ' defined but not used
+../fh_registry.h:543: warning: `fh_set_FSAMAXZ' defined but not used
+../fh_registry.h:543: warning: `fh_setval_FSAMAXZ' defined but not used
+../fh_registry.h:543: warning: `fh_setcmt_FSAMAXZ' defined but not used
+../fh_registry.h:543: warning: `fh_get_FSAMAXZ' defined but not used
+../fh_registry.h:543: warning: `fh_kw_FSAMAXZ' defined but not used
+../fh_registry.h:543: warning: `fh_cmt_FSAMAXZ' defined but not used
+../fh_registry.h:544: warning: `fh_set_FSAMEANZ' defined but not used
+../fh_registry.h:544: warning: `fh_setval_FSAMEANZ' defined but not used
+../fh_registry.h:544: warning: `fh_setcmt_FSAMEANZ' defined but not used
+../fh_registry.h:544: warning: `fh_get_FSAMEANZ' defined but not used
+../fh_registry.h:544: warning: `fh_kw_FSAMEANZ' defined but not used
+../fh_registry.h:544: warning: `fh_cmt_FSAMEANZ' defined but not used
+../fh_registry.h:545: warning: `fh_set_FSASTDVZ' defined but not used
+../fh_registry.h:545: warning: `fh_setval_FSASTDVZ' defined but not used
+../fh_registry.h:545: warning: `fh_setcmt_FSASTDVZ' defined but not used
+../fh_registry.h:545: warning: `fh_get_FSASTDVZ' defined but not used
+../fh_registry.h:545: warning: `fh_kw_FSASTDVZ' defined but not used
+../fh_registry.h:545: warning: `fh_cmt_FSASTDVZ' defined but not used
+../fh_registry.h:553: warning: `fh_set_TCSGPSBC' defined but not used
+../fh_registry.h:553: warning: `fh_setval_TCSGPSBC' defined but not used
+../fh_registry.h:553: warning: `fh_setcmt_TCSGPSBC' defined but not used
+../fh_registry.h:553: warning: `fh_get_TCSGPSBC' defined but not used
+../fh_registry.h:553: warning: `fh_kw_TCSGPSBC' defined but not used
+../fh_registry.h:553: warning: `fh_cmt_TCSGPSBC' defined but not used
+../fh_registry.h:554: warning: `fh_set_TCSGPSTM' defined but not used
+../fh_registry.h:554: warning: `fh_setval_TCSGPSTM' defined but not used
+../fh_registry.h:554: warning: `fh_setcmt_TCSGPSTM' defined but not used
+../fh_registry.h:554: warning: `fh_get_TCSGPSTM' defined but not used
+../fh_registry.h:554: warning: `fh_kw_TCSGPSTM' defined but not used
+../fh_registry.h:554: warning: `fh_cmt_TCSGPSTM' defined but not used
+../fh_registry.h:555: warning: `fh_set_TCSRBUSS' defined but not used
+../fh_registry.h:555: warning: `fh_setval_TCSRBUSS' defined but not used
+../fh_registry.h:555: warning: `fh_setcmt_TCSRBUSS' defined but not used
+../fh_registry.h:555: warning: `fh_get_TCSRBUSS' defined but not used
+../fh_registry.h:555: warning: `fh_kw_TCSRBUSS' defined but not used
+../fh_registry.h:555: warning: `fh_cmt_TCSRBUSS' defined but not used
+../fh_registry.h:556: warning: `fh_set_TCSEPICS' defined but not used
+../fh_registry.h:556: warning: `fh_setval_TCSEPICS' defined but not used
+../fh_registry.h:556: warning: `fh_setcmt_TCSEPICS' defined but not used
+../fh_registry.h:556: warning: `fh_get_TCSEPICS' defined but not used
+../fh_registry.h:556: warning: `fh_kw_TCSEPICS' defined but not used
+../fh_registry.h:556: warning: `fh_cmt_TCSEPICS' defined but not used
+../fh_registry.h:557: warning: `fh_set_TCSAMODE' defined but not used
+../fh_registry.h:557: warning: `fh_setval_TCSAMODE' defined but not used
+../fh_registry.h:557: warning: `fh_setcmt_TCSAMODE' defined but not used
+../fh_registry.h:557: warning: `fh_get_TCSAMODE' defined but not used
+../fh_registry.h:557: warning: `fh_kw_TCSAMODE' defined but not used
+../fh_registry.h:557: warning: `fh_cmt_TCSAMODE' defined but not used
+../fh_registry.h:558: warning: `fh_set_TCSMJD' defined but not used
+../fh_registry.h:558: warning: `fh_setval_TCSMJD' defined but not used
+../fh_registry.h:558: warning: `fh_setcmt_TCSMJD' defined but not used
+../fh_registry.h:558: warning: `fh_get_TCSMJD' defined but not used
+../fh_registry.h:558: warning: `fh_kw_TCSMJD' defined but not used
+../fh_registry.h:558: warning: `fh_cmt_TCSMJD' defined but not used
+../fh_registry.h:559: warning: `fh_set_TCSLST' defined but not used
+../fh_registry.h:559: warning: `fh_setval_TCSLST' defined but not used
+../fh_registry.h:559: warning: `fh_setcmt_TCSLST' defined but not used
+../fh_registry.h:559: warning: `fh_get_TCSLST' defined but not used
+../fh_registry.h:559: warning: `fh_kw_TCSLST' defined but not used
+../fh_registry.h:559: warning: `fh_cmt_TCSLST' defined but not used
+../fh_registry.h:560: warning: `fh_set_TCSAPHA' defined but not used
+../fh_registry.h:560: warning: `fh_setval_TCSAPHA' defined but not used
+../fh_registry.h:560: warning: `fh_setcmt_TCSAPHA' defined but not used
+../fh_registry.h:560: warning: `fh_get_TCSAPHA' defined but not used
+../fh_registry.h:560: warning: `fh_kw_TCSAPHA' defined but not used
+../fh_registry.h:560: warning: `fh_cmt_TCSAPHA' defined but not used
+../fh_registry.h:561: warning: `fh_set_TCSAPDEC' defined but not used
+../fh_registry.h:561: warning: `fh_setval_TCSAPDEC' defined but not used
+../fh_registry.h:561: warning: `fh_setcmt_TCSAPDEC' defined but not used
+../fh_registry.h:561: warning: `fh_get_TCSAPDEC' defined but not used
+../fh_registry.h:561: warning: `fh_kw_TCSAPDEC' defined but not used
+../fh_registry.h:561: warning: `fh_cmt_TCSAPDEC' defined but not used
+../fh_registry.h:562: warning: `fh_set_TCSOBHA' defined but not used
+../fh_registry.h:562: warning: `fh_setval_TCSOBHA' defined but not used
+../fh_registry.h:562: warning: `fh_setcmt_TCSOBHA' defined but not used
+../fh_registry.h:562: warning: `fh_get_TCSOBHA' defined but not used
+../fh_registry.h:562: warning: `fh_kw_TCSOBHA' defined but not used
+../fh_registry.h:562: warning: `fh_cmt_TCSOBHA' defined but not used
+../fh_registry.h:563: warning: `fh_set_TCSOBDEC' defined but not used
+../fh_registry.h:563: warning: `fh_setval_TCSOBDEC' defined but not used
+../fh_registry.h:563: warning: `fh_setcmt_TCSOBDEC' defined but not used
+../fh_registry.h:563: warning: `fh_get_TCSOBDEC' defined but not used
+../fh_registry.h:563: warning: `fh_kw_TCSOBDEC' defined but not used
+../fh_registry.h:563: warning: `fh_cmt_TCSOBDEC' defined but not used
+../fh_registry.h:564: warning: `fh_set_TCSENHA' defined but not used
+../fh_registry.h:564: warning: `fh_setval_TCSENHA' defined but not used
+../fh_registry.h:564: warning: `fh_setcmt_TCSENHA' defined but not used
+../fh_registry.h:564: warning: `fh_get_TCSENHA' defined but not used
+../fh_registry.h:564: warning: `fh_kw_TCSENHA' defined but not used
+../fh_registry.h:564: warning: `fh_cmt_TCSENHA' defined but not used
+../fh_registry.h:565: warning: `fh_set_TCSENDEC' defined but not used
+../fh_registry.h:565: warning: `fh_setval_TCSENDEC' defined but not used
+../fh_registry.h:565: warning: `fh_setcmt_TCSENDEC' defined but not used
+../fh_registry.h:565: warning: `fh_get_TCSENDEC' defined but not used
+../fh_registry.h:565: warning: `fh_kw_TCSENDEC' defined but not used
+../fh_registry.h:565: warning: `fh_cmt_TCSENDEC' defined but not used
+../fh_registry.h:566: warning: `fh_set_TCSEDHA' defined but not used
+../fh_registry.h:566: warning: `fh_setval_TCSEDHA' defined but not used
+../fh_registry.h:566: warning: `fh_setcmt_TCSEDHA' defined but not used
+../fh_registry.h:566: warning: `fh_get_TCSEDHA' defined but not used
+../fh_registry.h:566: warning: `fh_kw_TCSEDHA' defined but not used
+../fh_registry.h:566: warning: `fh_cmt_TCSEDHA' defined but not used
+../fh_registry.h:567: warning: `fh_set_TCSEDDEC' defined but not used
+../fh_registry.h:567: warning: `fh_setval_TCSEDDEC' defined but not used
+../fh_registry.h:567: warning: `fh_setcmt_TCSEDDEC' defined but not used
+../fh_registry.h:567: warning: `fh_get_TCSEDDEC' defined but not used
+../fh_registry.h:567: warning: `fh_kw_TCSEDDEC' defined but not used
+../fh_registry.h:567: warning: `fh_cmt_TCSEDDEC' defined but not used
+../fh_registry.h:568: warning: `fh_set_TCSMVRA' defined but not used
+../fh_registry.h:568: warning: `fh_setval_TCSMVRA' defined but not used
+../fh_registry.h:568: warning: `fh_setcmt_TCSMVRA' defined but not used
+../fh_registry.h:568: warning: `fh_get_TCSMVRA' defined but not used
+../fh_registry.h:568: warning: `fh_kw_TCSMVRA' defined but not used
+../fh_registry.h:568: warning: `fh_cmt_TCSMVRA' defined but not used
+../fh_registry.h:569: warning: `fh_set_TCSMVDEC' defined but not used
+../fh_registry.h:569: warning: `fh_setval_TCSMVDEC' defined but not used
+../fh_registry.h:569: warning: `fh_setcmt_TCSMVDEC' defined but not used
+../fh_registry.h:569: warning: `fh_get_TCSMVDEC' defined but not used
+../fh_registry.h:569: warning: `fh_kw_TCSMVDEC' defined but not used
+../fh_registry.h:569: warning: `fh_cmt_TCSMVDEC' defined but not used
+../fh_registry.h:570: warning: `fh_set_TCSMVX' defined but not used
+../fh_registry.h:570: warning: `fh_setval_TCSMVX' defined but not used
+../fh_registry.h:570: warning: `fh_setcmt_TCSMVX' defined but not used
+../fh_registry.h:570: warning: `fh_get_TCSMVX' defined but not used
+../fh_registry.h:570: warning: `fh_kw_TCSMVX' defined but not used
+../fh_registry.h:570: warning: `fh_cmt_TCSMVX' defined but not used
+../fh_registry.h:571: warning: `fh_set_TCSMVY' defined but not used
+../fh_registry.h:571: warning: `fh_setval_TCSMVY' defined but not used
+../fh_registry.h:571: warning: `fh_setcmt_TCSMVY' defined but not used
+../fh_registry.h:571: warning: `fh_get_TCSMVY' defined but not used
+../fh_registry.h:571: warning: `fh_kw_TCSMVY' defined but not used
+../fh_registry.h:571: warning: `fh_cmt_TCSMVY' defined but not used
+../fh_registry.h:579: warning: `fh_set_CMTAOB1' defined but not used
+../fh_registry.h:580: warning: `fh_set_CMTAOB2' defined but not used
+../fh_registry.h:581: warning: `fh_set_CMTAOB3' defined but not used
+../fh_registry.h:582: warning: `fh_set_CMTAOB4' defined but not used
+../fh_registry.h:590: warning: `fh_set_AOBLOOP' defined but not used
+../fh_registry.h:590: warning: `fh_setval_AOBLOOP' defined but not used
+../fh_registry.h:590: warning: `fh_setcmt_AOBLOOP' defined but not used
+../fh_registry.h:590: warning: `fh_get_AOBLOOP' defined but not used
+../fh_registry.h:590: warning: `fh_kw_AOBLOOP' defined but not used
+../fh_registry.h:590: warning: `fh_cmt_AOBLOOP' defined but not used
+../fh_registry.h:594: warning: `fh_set_LOOPGAIN' defined but not used
+../fh_registry.h:594: warning: `fh_setval_LOOPGAIN' defined but not used
+../fh_registry.h:594: warning: `fh_setcmt_LOOPGAIN' defined but not used
+../fh_registry.h:594: warning: `fh_get_LOOPGAIN' defined but not used
+../fh_registry.h:594: warning: `fh_kw_LOOPGAIN' defined but not used
+../fh_registry.h:594: warning: `fh_cmt_LOOPGAIN' defined but not used
+../fh_registry.h:601: warning: `fh_set_LOOPNES' defined but not used
+../fh_registry.h:601: warning: `fh_setval_LOOPNES' defined but not used
+../fh_registry.h:601: warning: `fh_setcmt_LOOPNES' defined but not used
+../fh_registry.h:601: warning: `fh_get_LOOPNES' defined but not used
+../fh_registry.h:601: warning: `fh_kw_LOOPNES' defined but not used
+../fh_registry.h:601: warning: `fh_cmt_LOOPNES' defined but not used
+../fh_registry.h:606: warning: `fh_set_LOOPNESG' defined but not used
+../fh_registry.h:606: warning: `fh_setval_LOOPNESG' defined but not used
+../fh_registry.h:606: warning: `fh_setcmt_LOOPNESG' defined but not used
+../fh_registry.h:606: warning: `fh_get_LOOPNESG' defined but not used
+../fh_registry.h:606: warning: `fh_kw_LOOPNESG' defined but not used
+../fh_registry.h:606: warning: `fh_cmt_LOOPNESG' defined but not used
+../fh_registry.h:611: warning: `fh_set_LOOPOPT' defined but not used
+../fh_registry.h:611: warning: `fh_setval_LOOPOPT' defined but not used
+../fh_registry.h:611: warning: `fh_setcmt_LOOPOPT' defined but not used
+../fh_registry.h:611: warning: `fh_get_LOOPOPT' defined but not used
+../fh_registry.h:611: warning: `fh_kw_LOOPOPT' defined but not used
+../fh_registry.h:611: warning: `fh_cmt_LOOPOPT' defined but not used
+../fh_registry.h:615: warning: `fh_set_WFSGOPT' defined but not used
+../fh_registry.h:615: warning: `fh_setval_WFSGOPT' defined but not used
+../fh_registry.h:615: warning: `fh_setcmt_WFSGOPT' defined but not used
+../fh_registry.h:615: warning: `fh_get_WFSGOPT' defined but not used
+../fh_registry.h:615: warning: `fh_kw_WFSGOPT' defined but not used
+../fh_registry.h:615: warning: `fh_cmt_WFSGOPT' defined but not used
+../fh_registry.h:619: warning: `fh_set_WFSSAMP' defined but not used
+../fh_registry.h:619: warning: `fh_setval_WFSSAMP' defined but not used
+../fh_registry.h:619: warning: `fh_setcmt_WFSSAMP' defined but not used
+../fh_registry.h:619: warning: `fh_get_WFSSAMP' defined but not used
+../fh_registry.h:619: warning: `fh_kw_WFSSAMP' defined but not used
+../fh_registry.h:619: warning: `fh_cmt_WFSSAMP' defined but not used
+../fh_registry.h:626: warning: `fh_set_R0' defined but not used
+../fh_registry.h:626: warning: `fh_setval_R0' defined but not used
+../fh_registry.h:626: warning: `fh_setcmt_R0' defined but not used
+../fh_registry.h:626: warning: `fh_get_R0' defined but not used
+../fh_registry.h:626: warning: `fh_kw_R0' defined but not used
+../fh_registry.h:626: warning: `fh_cmt_R0' defined but not used
+../fh_registry.h:630: warning: `fh_set_WFSCOUNT' defined but not used
+../fh_registry.h:630: warning: `fh_setval_WFSCOUNT' defined but not used
+../fh_registry.h:630: warning: `fh_setcmt_WFSCOUNT' defined but not used
+../fh_registry.h:630: warning: `fh_get_WFSCOUNT' defined but not used
+../fh_registry.h:630: warning: `fh_kw_WFSCOUNT' defined but not used
+../fh_registry.h:630: warning: `fh_cmt_WFSCOUNT' defined but not used
+../fh_registry.h:638: warning: `fh_set_ADCPOS' defined but not used
+../fh_registry.h:638: warning: `fh_setval_ADCPOS' defined but not used
+../fh_registry.h:638: warning: `fh_setcmt_ADCPOS' defined but not used
+../fh_registry.h:638: warning: `fh_get_ADCPOS' defined but not used
+../fh_registry.h:638: warning: `fh_kw_ADCPOS' defined but not used
+../fh_registry.h:638: warning: `fh_cmt_ADCPOS' defined but not used
+../fh_registry.h:642: warning: `fh_set_ADCANGLE' defined but not used
+../fh_registry.h:642: warning: `fh_setval_ADCANGLE' defined but not used
+../fh_registry.h:642: warning: `fh_setcmt_ADCANGLE' defined but not used
+../fh_registry.h:642: warning: `fh_get_ADCANGLE' defined but not used
+../fh_registry.h:642: warning: `fh_kw_ADCANGLE' defined but not used
+../fh_registry.h:642: warning: `fh_cmt_ADCANGLE' defined but not used
+../fh_registry.h:646: warning: `fh_set_ADCPOWER' defined but not used
+../fh_registry.h:646: warning: `fh_setval_ADCPOWER' defined but not used
+../fh_registry.h:646: warning: `fh_setcmt_ADCPOWER' defined but not used
+../fh_registry.h:646: warning: `fh_get_ADCPOWER' defined but not used
+../fh_registry.h:646: warning: `fh_kw_ADCPOWER' defined but not used
+../fh_registry.h:646: warning: `fh_cmt_ADCPOWER' defined but not used
+../fh_registry.h:650: warning: `fh_set_BEAMSPID' defined but not used
+../fh_registry.h:650: warning: `fh_setval_BEAMSPID' defined but not used
+../fh_registry.h:650: warning: `fh_setcmt_BEAMSPID' defined but not used
+../fh_registry.h:650: warning: `fh_get_BEAMSPID' defined but not used
+../fh_registry.h:650: warning: `fh_kw_BEAMSPID' defined but not used
+../fh_registry.h:650: warning: `fh_cmt_BEAMSPID' defined but not used
+../fh_registry.h:654: warning: `fh_set_BEAMSP' defined but not used
+../fh_registry.h:654: warning: `fh_setval_BEAMSP' defined but not used
+../fh_registry.h:654: warning: `fh_setcmt_BEAMSP' defined but not used
+../fh_registry.h:654: warning: `fh_get_BEAMSP' defined but not used
+../fh_registry.h:654: warning: `fh_kw_BEAMSP' defined but not used
+../fh_registry.h:654: warning: `fh_cmt_BEAMSP' defined but not used
+../fh_registry.h:659: warning: `fh_set_MIRSLIDE' defined but not used
+../fh_registry.h:659: warning: `fh_setval_MIRSLIDE' defined but not used
+../fh_registry.h:659: warning: `fh_setcmt_MIRSLIDE' defined but not used
+../fh_registry.h:659: warning: `fh_get_MIRSLIDE' defined but not used
+../fh_registry.h:659: warning: `fh_kw_MIRSLIDE' defined but not used
+../fh_registry.h:659: warning: `fh_cmt_MIRSLIDE' defined but not used
+../fh_registry.h:668: warning: `fh_set_WFSNDID' defined but not used
+../fh_registry.h:668: warning: `fh_setval_WFSNDID' defined but not used
+../fh_registry.h:668: warning: `fh_setcmt_WFSNDID' defined but not used
+../fh_registry.h:668: warning: `fh_get_WFSNDID' defined but not used
+../fh_registry.h:668: warning: `fh_kw_WFSNDID' defined but not used
+../fh_registry.h:668: warning: `fh_cmt_WFSNDID' defined but not used
+../fh_registry.h:673: warning: `fh_set_WFSX' defined but not used
+../fh_registry.h:673: warning: `fh_setval_WFSX' defined but not used
+../fh_registry.h:673: warning: `fh_setcmt_WFSX' defined but not used
+../fh_registry.h:673: warning: `fh_get_WFSX' defined but not used
+../fh_registry.h:673: warning: `fh_kw_WFSX' defined but not used
+../fh_registry.h:673: warning: `fh_cmt_WFSX' defined but not used
+../fh_registry.h:674: warning: `fh_set_WFSY' defined but not used
+../fh_registry.h:674: warning: `fh_setval_WFSY' defined but not used
+../fh_registry.h:674: warning: `fh_setcmt_WFSY' defined but not used
+../fh_registry.h:674: warning: `fh_get_WFSY' defined but not used
+../fh_registry.h:674: warning: `fh_kw_WFSY' defined but not used
+../fh_registry.h:674: warning: `fh_cmt_WFSY' defined but not used
+../fh_registry.h:675: warning: `fh_set_WFSZ' defined but not used
+../fh_registry.h:675: warning: `fh_setval_WFSZ' defined but not used
+../fh_registry.h:675: warning: `fh_setcmt_WFSZ' defined but not used
+../fh_registry.h:675: warning: `fh_get_WFSZ' defined but not used
+../fh_registry.h:675: warning: `fh_kw_WFSZ' defined but not used
+../fh_registry.h:675: warning: `fh_cmt_WFSZ' defined but not used
+../fh_registry.h:682: warning: `fh_set_CMTCAL1' defined but not used
+../fh_registry.h:683: warning: `fh_set_CMTCAL2' defined but not used
+../fh_registry.h:684: warning: `fh_set_CMTCAL3' defined but not used
+../fh_registry.h:685: warning: `fh_set_CMTCAL4' defined but not used
+../fh_registry.h:693: warning: `fh_set_FFLAMPON' defined but not used
+../fh_registry.h:693: warning: `fh_setval_FFLAMPON' defined but not used
+../fh_registry.h:693: warning: `fh_setcmt_FFLAMPON' defined but not used
+../fh_registry.h:693: warning: `fh_get_FFLAMPON' defined but not used
+../fh_registry.h:693: warning: `fh_kw_FFLAMPON' defined but not used
+../fh_registry.h:693: warning: `fh_cmt_FFLAMPON' defined but not used
+../fh_registry.h:694: warning: `fh_set_FFLAMP' defined but not used
+../fh_registry.h:694: warning: `fh_setval_FFLAMP' defined but not used
+../fh_registry.h:694: warning: `fh_setcmt_FFLAMP' defined but not used
+../fh_registry.h:694: warning: `fh_get_FFLAMP' defined but not used
+../fh_registry.h:694: warning: `fh_kw_FFLAMP' defined but not used
+../fh_registry.h:694: warning: `fh_cmt_FFLAMP' defined but not used
+../fh_registry.h:702: warning: `fh_set_CLAMP0ON' defined but not used
+../fh_registry.h:702: warning: `fh_setval_CLAMP0ON' defined but not used
+../fh_registry.h:702: warning: `fh_setcmt_CLAMP0ON' defined but not used
+../fh_registry.h:702: warning: `fh_get_CLAMP0ON' defined but not used
+../fh_registry.h:702: warning: `fh_kw_CLAMP0ON' defined but not used
+../fh_registry.h:702: warning: `fh_cmt_CLAMP0ON' defined but not used
+../fh_registry.h:703: warning: `fh_set_CLAMP0' defined but not used
+../fh_registry.h:703: warning: `fh_setval_CLAMP0' defined but not used
+../fh_registry.h:703: warning: `fh_setcmt_CLAMP0' defined but not used
+../fh_registry.h:703: warning: `fh_get_CLAMP0' defined but not used
+../fh_registry.h:703: warning: `fh_kw_CLAMP0' defined but not used
+../fh_registry.h:703: warning: `fh_cmt_CLAMP0' defined but not used
+../fh_registry.h:704: warning: `fh_set_CLAMP1ON' defined but not used
+../fh_registry.h:704: warning: `fh_setval_CLAMP1ON' defined but not used
+../fh_registry.h:704: warning: `fh_setcmt_CLAMP1ON' defined but not used
+../fh_registry.h:704: warning: `fh_get_CLAMP1ON' defined but not used
+../fh_registry.h:704: warning: `fh_kw_CLAMP1ON' defined but not used
+../fh_registry.h:704: warning: `fh_cmt_CLAMP1ON' defined but not used
+../fh_registry.h:705: warning: `fh_set_CLAMP1' defined but not used
+../fh_registry.h:705: warning: `fh_setval_CLAMP1' defined but not used
+../fh_registry.h:705: warning: `fh_setcmt_CLAMP1' defined but not used
+../fh_registry.h:705: warning: `fh_get_CLAMP1' defined but not used
+../fh_registry.h:705: warning: `fh_kw_CLAMP1' defined but not used
+../fh_registry.h:705: warning: `fh_cmt_CLAMP1' defined but not used
+../fh_registry.h:706: warning: `fh_set_CLAMP2ON' defined but not used
+../fh_registry.h:706: warning: `fh_setval_CLAMP2ON' defined but not used
+../fh_registry.h:706: warning: `fh_setcmt_CLAMP2ON' defined but not used
+../fh_registry.h:706: warning: `fh_get_CLAMP2ON' defined but not used
+../fh_registry.h:706: warning: `fh_kw_CLAMP2ON' defined but not used
+../fh_registry.h:706: warning: `fh_cmt_CLAMP2ON' defined but not used
+../fh_registry.h:707: warning: `fh_set_CLAMP2' defined but not used
+../fh_registry.h:707: warning: `fh_setval_CLAMP2' defined but not used
+../fh_registry.h:707: warning: `fh_setcmt_CLAMP2' defined but not used
+../fh_registry.h:707: warning: `fh_get_CLAMP2' defined but not used
+../fh_registry.h:707: warning: `fh_kw_CLAMP2' defined but not used
+../fh_registry.h:707: warning: `fh_cmt_CLAMP2' defined but not used
+../fh_registry.h:708: warning: `fh_set_CLAMP3ON' defined but not used
+../fh_registry.h:708: warning: `fh_setval_CLAMP3ON' defined but not used
+../fh_registry.h:708: warning: `fh_setcmt_CLAMP3ON' defined but not used
+../fh_registry.h:708: warning: `fh_get_CLAMP3ON' defined but not used
+../fh_registry.h:708: warning: `fh_kw_CLAMP3ON' defined but not used
+../fh_registry.h:708: warning: `fh_cmt_CLAMP3ON' defined but not used
+../fh_registry.h:709: warning: `fh_set_CLAMP3' defined but not used
+../fh_registry.h:709: warning: `fh_setval_CLAMP3' defined but not used
+../fh_registry.h:709: warning: `fh_setcmt_CLAMP3' defined but not used
+../fh_registry.h:709: warning: `fh_get_CLAMP3' defined but not used
+../fh_registry.h:709: warning: `fh_kw_CLAMP3' defined but not used
+../fh_registry.h:709: warning: `fh_cmt_CLAMP3' defined but not used
+../fh_registry.h:716: warning: `fh_set_CALIBL0' defined but not used
+../fh_registry.h:716: warning: `fh_setval_CALIBL0' defined but not used
+../fh_registry.h:716: warning: `fh_setcmt_CALIBL0' defined but not used
+../fh_registry.h:716: warning: `fh_get_CALIBL0' defined but not used
+../fh_registry.h:716: warning: `fh_kw_CALIBL0' defined but not used
+../fh_registry.h:716: warning: `fh_cmt_CALIBL0' defined but not used
+../fh_registry.h:717: warning: `fh_set_CALIBL1' defined but not used
+../fh_registry.h:717: warning: `fh_setval_CALIBL1' defined but not used
+../fh_registry.h:717: warning: `fh_setcmt_CALIBL1' defined but not used
+../fh_registry.h:717: warning: `fh_get_CALIBL1' defined but not used
+../fh_registry.h:717: warning: `fh_kw_CALIBL1' defined but not used
+../fh_registry.h:717: warning: `fh_cmt_CALIBL1' defined but not used
+../fh_registry.h:718: warning: `fh_set_CALIBL2' defined but not used
+../fh_registry.h:718: warning: `fh_setval_CALIBL2' defined but not used
+../fh_registry.h:718: warning: `fh_setcmt_CALIBL2' defined but not used
+../fh_registry.h:718: warning: `fh_get_CALIBL2' defined but not used
+../fh_registry.h:718: warning: `fh_kw_CALIBL2' defined but not used
+../fh_registry.h:718: warning: `fh_cmt_CALIBL2' defined but not used
+../fh_registry.h:719: warning: `fh_set_CALIBL3' defined but not used
+../fh_registry.h:719: warning: `fh_setval_CALIBL3' defined but not used
+../fh_registry.h:719: warning: `fh_setcmt_CALIBL3' defined but not used
+../fh_registry.h:719: warning: `fh_get_CALIBL3' defined but not used
+../fh_registry.h:719: warning: `fh_kw_CALIBL3' defined but not used
+../fh_registry.h:719: warning: `fh_cmt_CALIBL3' defined but not used
+../fh_registry.h:720: warning: `fh_set_CALIBL4' defined but not used
+../fh_registry.h:720: warning: `fh_setval_CALIBL4' defined but not used
+../fh_registry.h:720: warning: `fh_setcmt_CALIBL4' defined but not used
+../fh_registry.h:720: warning: `fh_get_CALIBL4' defined but not used
+../fh_registry.h:720: warning: `fh_kw_CALIBL4' defined but not used
+../fh_registry.h:720: warning: `fh_cmt_CALIBL4' defined but not used
+../fh_registry.h:721: warning: `fh_set_CALIBL5' defined but not used
+../fh_registry.h:721: warning: `fh_setval_CALIBL5' defined but not used
+../fh_registry.h:721: warning: `fh_setcmt_CALIBL5' defined but not used
+../fh_registry.h:721: warning: `fh_get_CALIBL5' defined but not used
+../fh_registry.h:721: warning: `fh_kw_CALIBL5' defined but not used
+../fh_registry.h:721: warning: `fh_cmt_CALIBL5' defined but not used
+../fh_registry.h:722: warning: `fh_set_CALIBL6' defined but not used
+../fh_registry.h:722: warning: `fh_setval_CALIBL6' defined but not used
+../fh_registry.h:722: warning: `fh_setcmt_CALIBL6' defined but not used
+../fh_registry.h:722: warning: `fh_get_CALIBL6' defined but not used
+../fh_registry.h:722: warning: `fh_kw_CALIBL6' defined but not used
+../fh_registry.h:722: warning: `fh_cmt_CALIBL6' defined but not used
+../fh_registry.h:723: warning: `fh_set_CALIBL7' defined but not used
+../fh_registry.h:723: warning: `fh_setval_CALIBL7' defined but not used
+../fh_registry.h:723: warning: `fh_setcmt_CALIBL7' defined but not used
+../fh_registry.h:723: warning: `fh_get_CALIBL7' defined but not used
+../fh_registry.h:723: warning: `fh_kw_CALIBL7' defined but not used
+../fh_registry.h:723: warning: `fh_cmt_CALIBL7' defined but not used
+../fh_registry.h:724: warning: `fh_set_CALIBL8' defined but not used
+../fh_registry.h:724: warning: `fh_setval_CALIBL8' defined but not used
+../fh_registry.h:724: warning: `fh_setcmt_CALIBL8' defined but not used
+../fh_registry.h:724: warning: `fh_get_CALIBL8' defined but not used
+../fh_registry.h:724: warning: `fh_kw_CALIBL8' defined but not used
+../fh_registry.h:724: warning: `fh_cmt_CALIBL8' defined but not used
+../fh_registry.h:725: warning: `fh_set_CALIBL9' defined but not used
+../fh_registry.h:725: warning: `fh_setval_CALIBL9' defined but not used
+../fh_registry.h:725: warning: `fh_setcmt_CALIBL9' defined but not used
+../fh_registry.h:725: warning: `fh_get_CALIBL9' defined but not used
+../fh_registry.h:725: warning: `fh_kw_CALIBL9' defined but not used
+../fh_registry.h:725: warning: `fh_cmt_CALIBL9' defined but not used
+../fh_registry.h:732: warning: `fh_set_CMTINST1' defined but not used
+../fh_registry.h:733: warning: `fh_set_CMTINST2' defined but not used
+../fh_registry.h:734: warning: `fh_set_CMTINST3' defined but not used
+../fh_registry.h:735: warning: `fh_set_CMTINST4' defined but not used
+../fh_registry.h:766: warning: `fh_set_FILTERID' defined but not used
+../fh_registry.h:766: warning: `fh_setval_FILTERID' defined but not used
+../fh_registry.h:766: warning: `fh_setcmt_FILTERID' defined but not used
+../fh_registry.h:766: warning: `fh_get_FILTERID' defined but not used
+../fh_registry.h:766: warning: `fh_kw_FILTERID' defined but not used
+../fh_registry.h:766: warning: `fh_cmt_FILTERID' defined but not used
+../fh_registry.h:767: warning: `fh_set_FILTER' defined but not used
+../fh_registry.h:767: warning: `fh_setval_FILTER' defined but not used
+../fh_registry.h:767: warning: `fh_setcmt_FILTER' defined but not used
+../fh_registry.h:767: warning: `fh_get_FILTER' defined but not used
+../fh_registry.h:767: warning: `fh_kw_FILTER' defined but not used
+../fh_registry.h:767: warning: `fh_cmt_FILTER' defined but not used
+../fh_registry.h:768: warning: `fh_set_FILTERBW' defined but not used
+../fh_registry.h:768: warning: `fh_setval_FILTERBW' defined but not used
+../fh_registry.h:768: warning: `fh_setcmt_FILTERBW' defined but not used
+../fh_registry.h:768: warning: `fh_get_FILTERBW' defined but not used
+../fh_registry.h:768: warning: `fh_kw_FILTERBW' defined but not used
+../fh_registry.h:768: warning: `fh_cmt_FILTERBW' defined but not used
+../fh_registry.h:769: warning: `fh_set_FILTERWL' defined but not used
+../fh_registry.h:769: warning: `fh_setval_FILTERWL' defined but not used
+../fh_registry.h:769: warning: `fh_setcmt_FILTERWL' defined but not used
+../fh_registry.h:769: warning: `fh_get_FILTERWL' defined but not used
+../fh_registry.h:769: warning: `fh_kw_FILTERWL' defined but not used
+../fh_registry.h:769: warning: `fh_cmt_FILTERWL' defined but not used
+../fh_registry.h:770: warning: `fh_set_FILTERLB' defined but not used
+../fh_registry.h:770: warning: `fh_setval_FILTERLB' defined but not used
+../fh_registry.h:770: warning: `fh_setcmt_FILTERLB' defined but not used
+../fh_registry.h:770: warning: `fh_get_FILTERLB' defined but not used
+../fh_registry.h:770: warning: `fh_kw_FILTERLB' defined but not used
+../fh_registry.h:770: warning: `fh_cmt_FILTERLB' defined but not used
+../fh_registry.h:771: warning: `fh_set_FILTERUB' defined but not used
+../fh_registry.h:771: warning: `fh_setval_FILTERUB' defined but not used
+../fh_registry.h:771: warning: `fh_setcmt_FILTERUB' defined but not used
+../fh_registry.h:771: warning: `fh_get_FILTERUB' defined but not used
+../fh_registry.h:771: warning: `fh_kw_FILTERUB' defined but not used
+../fh_registry.h:771: warning: `fh_cmt_FILTERUB' defined but not used
+../fh_registry.h:773: warning: `fh_set_FILTSLID' defined but not used
+../fh_registry.h:773: warning: `fh_setval_FILTSLID' defined but not used
+../fh_registry.h:773: warning: `fh_setcmt_FILTSLID' defined but not used
+../fh_registry.h:773: warning: `fh_get_FILTSLID' defined but not used
+../fh_registry.h:773: warning: `fh_kw_FILTSLID' defined but not used
+../fh_registry.h:773: warning: `fh_cmt_FILTSLID' defined but not used
+../fh_registry.h:776: warning: `fh_set_WHEELAID' defined but not used
+../fh_registry.h:776: warning: `fh_setval_WHEELAID' defined but not used
+../fh_registry.h:776: warning: `fh_setcmt_WHEELAID' defined but not used
+../fh_registry.h:776: warning: `fh_get_WHEELAID' defined but not used
+../fh_registry.h:776: warning: `fh_kw_WHEELAID' defined but not used
+../fh_registry.h:776: warning: `fh_cmt_WHEELAID' defined but not used
+../fh_registry.h:777: warning: `fh_set_WHEELADE' defined but not used
+../fh_registry.h:777: warning: `fh_setval_WHEELADE' defined but not used
+../fh_registry.h:777: warning: `fh_setcmt_WHEELADE' defined but not used
+../fh_registry.h:777: warning: `fh_get_WHEELADE' defined but not used
+../fh_registry.h:777: warning: `fh_kw_WHEELADE' defined but not used
+../fh_registry.h:777: warning: `fh_cmt_WHEELADE' defined but not used
+../fh_registry.h:778: warning: `fh_set_WHEELALB' defined but not used
+../fh_registry.h:778: warning: `fh_setval_WHEELALB' defined but not used
+../fh_registry.h:778: warning: `fh_setcmt_WHEELALB' defined but not used
+../fh_registry.h:778: warning: `fh_get_WHEELALB' defined but not used
+../fh_registry.h:778: warning: `fh_kw_WHEELALB' defined but not used
+../fh_registry.h:778: warning: `fh_cmt_WHEELALB' defined but not used
+../fh_registry.h:779: warning: `fh_set_WHEELAUB' defined but not used
+../fh_registry.h:779: warning: `fh_setval_WHEELAUB' defined but not used
+../fh_registry.h:779: warning: `fh_setcmt_WHEELAUB' defined but not used
+../fh_registry.h:779: warning: `fh_get_WHEELAUB' defined but not used
+../fh_registry.h:779: warning: `fh_kw_WHEELAUB' defined but not used
+../fh_registry.h:779: warning: `fh_cmt_WHEELAUB' defined but not used
+../fh_registry.h:781: warning: `fh_set_WHEELBID' defined but not used
+../fh_registry.h:781: warning: `fh_setval_WHEELBID' defined but not used
+../fh_registry.h:781: warning: `fh_setcmt_WHEELBID' defined but not used
+../fh_registry.h:781: warning: `fh_get_WHEELBID' defined but not used
+../fh_registry.h:781: warning: `fh_kw_WHEELBID' defined but not used
+../fh_registry.h:781: warning: `fh_cmt_WHEELBID' defined but not used
+../fh_registry.h:782: warning: `fh_set_WHEELBDE' defined but not used
+../fh_registry.h:782: warning: `fh_setval_WHEELBDE' defined but not used
+../fh_registry.h:782: warning: `fh_setcmt_WHEELBDE' defined but not used
+../fh_registry.h:782: warning: `fh_get_WHEELBDE' defined but not used
+../fh_registry.h:782: warning: `fh_kw_WHEELBDE' defined but not used
+../fh_registry.h:782: warning: `fh_cmt_WHEELBDE' defined but not used
+../fh_registry.h:783: warning: `fh_set_WHEELBLB' defined but not used
+../fh_registry.h:783: warning: `fh_setval_WHEELBLB' defined but not used
+../fh_registry.h:783: warning: `fh_setcmt_WHEELBLB' defined but not used
+../fh_registry.h:783: warning: `fh_get_WHEELBLB' defined but not used
+../fh_registry.h:783: warning: `fh_kw_WHEELBLB' defined but not used
+../fh_registry.h:783: warning: `fh_cmt_WHEELBLB' defined but not used
+../fh_registry.h:784: warning: `fh_set_WHEELBUB' defined but not used
+../fh_registry.h:784: warning: `fh_setval_WHEELBUB' defined but not used
+../fh_registry.h:784: warning: `fh_setcmt_WHEELBUB' defined but not used
+../fh_registry.h:784: warning: `fh_get_WHEELBUB' defined but not used
+../fh_registry.h:784: warning: `fh_kw_WHEELBUB' defined but not used
+../fh_registry.h:784: warning: `fh_cmt_WHEELBUB' defined but not used
+../fh_registry.h:792: warning: `fh_set_INSTFOC' defined but not used
+../fh_registry.h:792: warning: `fh_setval_INSTFOC' defined but not used
+../fh_registry.h:792: warning: `fh_setcmt_INSTFOC' defined but not used
+../fh_registry.h:792: warning: `fh_get_INSTFOC' defined but not used
+../fh_registry.h:792: warning: `fh_kw_INSTFOC' defined but not used
+../fh_registry.h:792: warning: `fh_cmt_INSTFOC' defined but not used
+../fh_registry.h:800: warning: `fh_set_CMTSPEC1' defined but not used
+../fh_registry.h:801: warning: `fh_set_CMTSPEC2' defined but not used
+../fh_registry.h:802: warning: `fh_set_CMTSPEC3' defined but not used
+../fh_registry.h:803: warning: `fh_set_CMTSPEC4' defined but not used
+../fh_registry.h:815: warning: `fh_set_GRISMID' defined but not used
+../fh_registry.h:815: warning: `fh_setval_GRISMID' defined but not used
+../fh_registry.h:815: warning: `fh_setcmt_GRISMID' defined but not used
+../fh_registry.h:815: warning: `fh_get_GRISMID' defined but not used
+../fh_registry.h:815: warning: `fh_kw_GRISMID' defined but not used
+../fh_registry.h:815: warning: `fh_cmt_GRISMID' defined but not used
+../fh_registry.h:816: warning: `fh_set_GRISM' defined but not used
+../fh_registry.h:816: warning: `fh_setval_GRISM' defined but not used
+../fh_registry.h:816: warning: `fh_setcmt_GRISM' defined but not used
+../fh_registry.h:816: warning: `fh_get_GRISM' defined but not used
+../fh_registry.h:816: warning: `fh_kw_GRISM' defined but not used
+../fh_registry.h:816: warning: `fh_cmt_GRISM' defined but not used
+../fh_registry.h:817: warning: `fh_set_GRISSLID' defined but not used
+../fh_registry.h:817: warning: `fh_setval_GRISSLID' defined but not used
+../fh_registry.h:817: warning: `fh_setcmt_GRISSLID' defined but not used
+../fh_registry.h:817: warning: `fh_get_GRISSLID' defined but not used
+../fh_registry.h:817: warning: `fh_kw_GRISSLID' defined but not used
+../fh_registry.h:817: warning: `fh_cmt_GRISSLID' defined but not used
+../fh_registry.h:818: warning: `fh_set_MASKID' defined but not used
+../fh_registry.h:818: warning: `fh_setval_MASKID' defined but not used
+../fh_registry.h:818: warning: `fh_setcmt_MASKID' defined but not used
+../fh_registry.h:818: warning: `fh_get_MASKID' defined but not used
+../fh_registry.h:818: warning: `fh_kw_MASKID' defined but not used
+../fh_registry.h:818: warning: `fh_cmt_MASKID' defined but not used
+../fh_registry.h:819: warning: `fh_set_MASK' defined but not used
+../fh_registry.h:819: warning: `fh_setval_MASK' defined but not used
+../fh_registry.h:819: warning: `fh_setcmt_MASK' defined but not used
+../fh_registry.h:819: warning: `fh_get_MASK' defined but not used
+../fh_registry.h:819: warning: `fh_kw_MASK' defined but not used
+../fh_registry.h:819: warning: `fh_cmt_MASK' defined but not used
+../fh_registry.h:820: warning: `fh_set_MASKSLID' defined but not used
+../fh_registry.h:820: warning: `fh_setval_MASKSLID' defined but not used
+../fh_registry.h:820: warning: `fh_setcmt_MASKSLID' defined but not used
+../fh_registry.h:820: warning: `fh_get_MASKSLID' defined but not used
+../fh_registry.h:820: warning: `fh_kw_MASKSLID' defined but not used
+../fh_registry.h:820: warning: `fh_cmt_MASKSLID' defined but not used
+../fh_registry.h:827: warning: `fh_set_DISPEL' defined but not used
+../fh_registry.h:827: warning: `fh_setval_DISPEL' defined but not used
+../fh_registry.h:827: warning: `fh_setcmt_DISPEL' defined but not used
+../fh_registry.h:827: warning: `fh_get_DISPEL' defined but not used
+../fh_registry.h:827: warning: `fh_kw_DISPEL' defined but not used
+../fh_registry.h:827: warning: `fh_cmt_DISPEL' defined but not used
+../fh_registry.h:831: warning: `fh_set_DISPAXIS' defined but not used
+../fh_registry.h:831: warning: `fh_setval_DISPAXIS' defined but not used
+../fh_registry.h:831: warning: `fh_setcmt_DISPAXIS' defined but not used
+../fh_registry.h:831: warning: `fh_get_DISPAXIS' defined but not used
+../fh_registry.h:831: warning: `fh_kw_DISPAXIS' defined but not used
+../fh_registry.h:831: warning: `fh_cmt_DISPAXIS' defined but not used
+../fh_registry.h:835: warning: `fh_set_DISPANG' defined but not used
+../fh_registry.h:835: warning: `fh_setval_DISPANG' defined but not used
+../fh_registry.h:835: warning: `fh_setcmt_DISPANG' defined but not used
+../fh_registry.h:835: warning: `fh_get_DISPANG' defined but not used
+../fh_registry.h:835: warning: `fh_kw_DISPANG' defined but not used
+../fh_registry.h:835: warning: `fh_cmt_DISPANG' defined but not used
+../fh_registry.h:839: warning: `fh_set_WAVELENG' defined but not used
+../fh_registry.h:839: warning: `fh_setval_WAVELENG' defined but not used
+../fh_registry.h:839: warning: `fh_setcmt_WAVELENG' defined but not used
+../fh_registry.h:839: warning: `fh_get_WAVELENG' defined but not used
+../fh_registry.h:839: warning: `fh_kw_WAVELENG' defined but not used
+../fh_registry.h:839: warning: `fh_cmt_WAVELENG' defined but not used
+../fh_registry.h:843: warning: `fh_set_ORDER' defined but not used
+../fh_registry.h:843: warning: `fh_setval_ORDER' defined but not used
+../fh_registry.h:843: warning: `fh_setcmt_ORDER' defined but not used
+../fh_registry.h:843: warning: `fh_get_ORDER' defined but not used
+../fh_registry.h:843: warning: `fh_kw_ORDER' defined but not used
+../fh_registry.h:843: warning: `fh_cmt_ORDER' defined but not used
+../fh_registry.h:847: warning: `fh_set_GRSETUP' defined but not used
+../fh_registry.h:847: warning: `fh_setval_GRSETUP' defined but not used
+../fh_registry.h:847: warning: `fh_setcmt_GRSETUP' defined but not used
+../fh_registry.h:847: warning: `fh_get_GRSETUP' defined but not used
+../fh_registry.h:847: warning: `fh_kw_GRSETUP' defined but not used
+../fh_registry.h:847: warning: `fh_cmt_GRSETUP' defined but not used
+../fh_registry.h:851: warning: `fh_set_OPENANG2' defined but not used
+../fh_registry.h:851: warning: `fh_setval_OPENANG2' defined but not used
+../fh_registry.h:851: warning: `fh_setcmt_OPENANG2' defined but not used
+../fh_registry.h:851: warning: `fh_get_OPENANG2' defined but not used
+../fh_registry.h:851: warning: `fh_kw_OPENANG2' defined but not used
+../fh_registry.h:851: warning: `fh_cmt_OPENANG2' defined but not used
+../fh_registry.h:855: warning: `fh_set_GRISMANG' defined but not used
+../fh_registry.h:855: warning: `fh_setval_GRISMANG' defined but not used
+../fh_registry.h:855: warning: `fh_setcmt_GRISMANG' defined but not used
+../fh_registry.h:855: warning: `fh_get_GRISMANG' defined but not used
+../fh_registry.h:855: warning: `fh_kw_GRISMANG' defined but not used
+../fh_registry.h:855: warning: `fh_cmt_GRISMANG' defined but not used
+../fh_registry.h:860: warning: `fh_set_SLICER' defined but not used
+../fh_registry.h:860: warning: `fh_setval_SLICER' defined but not used
+../fh_registry.h:860: warning: `fh_setcmt_SLICER' defined but not used
+../fh_registry.h:860: warning: `fh_get_SLICER' defined but not used
+../fh_registry.h:860: warning: `fh_kw_SLICER' defined but not used
+../fh_registry.h:860: warning: `fh_cmt_SLICER' defined but not used
+../fh_registry.h:865: warning: `fh_set_HART1A' defined but not used
+../fh_registry.h:865: warning: `fh_setval_HART1A' defined but not used
+../fh_registry.h:865: warning: `fh_setcmt_HART1A' defined but not used
+../fh_registry.h:865: warning: `fh_get_HART1A' defined but not used
+../fh_registry.h:865: warning: `fh_kw_HART1A' defined but not used
+../fh_registry.h:865: warning: `fh_cmt_HART1A' defined but not used
+../fh_registry.h:866: warning: `fh_set_HART1B' defined but not used
+../fh_registry.h:866: warning: `fh_setval_HART1B' defined but not used
+../fh_registry.h:866: warning: `fh_setcmt_HART1B' defined but not used
+../fh_registry.h:866: warning: `fh_get_HART1B' defined but not used
+../fh_registry.h:866: warning: `fh_kw_HART1B' defined but not used
+../fh_registry.h:866: warning: `fh_cmt_HART1B' defined but not used
+../fh_registry.h:867: warning: `fh_set_HART2' defined but not used
+../fh_registry.h:867: warning: `fh_setval_HART2' defined but not used
+../fh_registry.h:867: warning: `fh_setcmt_HART2' defined but not used
+../fh_registry.h:867: warning: `fh_get_HART2' defined but not used
+../fh_registry.h:867: warning: `fh_kw_HART2' defined but not used
+../fh_registry.h:867: warning: `fh_cmt_HART2' defined but not used
+../fh_registry.h:868: warning: `fh_set_HART3' defined but not used
+../fh_registry.h:868: warning: `fh_setval_HART3' defined but not used
+../fh_registry.h:868: warning: `fh_setcmt_HART3' defined but not used
+../fh_registry.h:868: warning: `fh_get_HART3' defined but not used
+../fh_registry.h:868: warning: `fh_kw_HART3' defined but not used
+../fh_registry.h:868: warning: `fh_cmt_HART3' defined but not used
+../fh_registry.h:869: warning: `fh_set_HART4' defined but not used
+../fh_registry.h:869: warning: `fh_setval_HART4' defined but not used
+../fh_registry.h:869: warning: `fh_setcmt_HART4' defined but not used
+../fh_registry.h:869: warning: `fh_get_HART4' defined but not used
+../fh_registry.h:869: warning: `fh_kw_HART4' defined but not used
+../fh_registry.h:869: warning: `fh_cmt_HART4' defined but not used
+../fh_registry.h:875: warning: `fh_set_COUDETRN' defined but not used
+../fh_registry.h:875: warning: `fh_setval_COUDETRN' defined but not used
+../fh_registry.h:875: warning: `fh_setcmt_COUDETRN' defined but not used
+../fh_registry.h:875: warning: `fh_get_COUDETRN' defined but not used
+../fh_registry.h:875: warning: `fh_kw_COUDETRN' defined but not used
+../fh_registry.h:875: warning: `fh_cmt_COUDETRN' defined but not used
+../fh_registry.h:880: warning: `fh_set_EMFILTER' defined but not used
+../fh_registry.h:880: warning: `fh_setval_EMFILTER' defined but not used
+../fh_registry.h:880: warning: `fh_setcmt_EMFILTER' defined but not used
+../fh_registry.h:880: warning: `fh_get_EMFILTER' defined but not used
+../fh_registry.h:880: warning: `fh_kw_EMFILTER' defined but not used
+../fh_registry.h:880: warning: `fh_cmt_EMFILTER' defined but not used
+../fh_registry.h:881: warning: `fh_set_EMCNTS' defined but not used
+../fh_registry.h:881: warning: `fh_setval_EMCNTS' defined but not used
+../fh_registry.h:881: warning: `fh_setcmt_EMCNTS' defined but not used
+../fh_registry.h:881: warning: `fh_get_EMCNTS' defined but not used
+../fh_registry.h:881: warning: `fh_kw_EMCNTS' defined but not used
+../fh_registry.h:881: warning: `fh_cmt_EMCNTS' defined but not used
+../fh_registry.h:882: warning: `fh_set_MIDEXPTM' defined but not used
+../fh_registry.h:882: warning: `fh_setval_MIDEXPTM' defined but not used
+../fh_registry.h:882: warning: `fh_setcmt_MIDEXPTM' defined but not used
+../fh_registry.h:882: warning: `fh_get_MIDEXPTM' defined but not used
+../fh_registry.h:882: warning: `fh_kw_MIDEXPTM' defined but not used
+../fh_registry.h:882: warning: `fh_cmt_MIDEXPTM' defined but not used
+../fh_registry.h:889: warning: `fh_set_CAFE' defined but not used
+../fh_registry.h:889: warning: `fh_setval_CAFE' defined but not used
+../fh_registry.h:889: warning: `fh_setcmt_CAFE' defined but not used
+../fh_registry.h:889: warning: `fh_get_CAFE' defined but not used
+../fh_registry.h:889: warning: `fh_kw_CAFE' defined but not used
+../fh_registry.h:889: warning: `fh_cmt_CAFE' defined but not used
+../fh_registry.h:897: warning: `fh_set_AUXINST' defined but not used
+../fh_registry.h:897: warning: `fh_setval_AUXINST' defined but not used
+../fh_registry.h:897: warning: `fh_setcmt_AUXINST' defined but not used
+../fh_registry.h:897: warning: `fh_get_AUXINST' defined but not used
+../fh_registry.h:897: warning: `fh_kw_AUXINST' defined but not used
+../fh_registry.h:897: warning: `fh_cmt_AUXINST' defined but not used
+../fh_registry.h:901: warning: `fh_set_CONST' defined but not used
+../fh_registry.h:901: warning: `fh_setval_CONST' defined but not used
+../fh_registry.h:901: warning: `fh_setcmt_CONST' defined but not used
+../fh_registry.h:901: warning: `fh_get_CONST' defined but not used
+../fh_registry.h:901: warning: `fh_kw_CONST' defined but not used
+../fh_registry.h:901: warning: `fh_cmt_CONST' defined but not used
+../fh_registry.h:905: warning: `fh_set_NUMCHAN' defined but not used
+../fh_registry.h:905: warning: `fh_setval_NUMCHAN' defined but not used
+../fh_registry.h:905: warning: `fh_setcmt_NUMCHAN' defined but not used
+../fh_registry.h:905: warning: `fh_get_NUMCHAN' defined but not used
+../fh_registry.h:905: warning: `fh_kw_NUMCHAN' defined but not used
+../fh_registry.h:905: warning: `fh_cmt_NUMCHAN' defined but not used
+../fh_registry.h:909: warning: `fh_set_CURCHAN' defined but not used
+../fh_registry.h:909: warning: `fh_setval_CURCHAN' defined but not used
+../fh_registry.h:909: warning: `fh_setcmt_CURCHAN' defined but not used
+../fh_registry.h:909: warning: `fh_get_CURCHAN' defined but not used
+../fh_registry.h:909: warning: `fh_kw_CURCHAN' defined but not used
+../fh_registry.h:909: warning: `fh_cmt_CURCHAN' defined but not used
+../fh_registry.h:913: warning: `fh_set_BINVAL' defined but not used
+../fh_registry.h:913: warning: `fh_setval_BINVAL' defined but not used
+../fh_registry.h:913: warning: `fh_setcmt_BINVAL' defined but not used
+../fh_registry.h:913: warning: `fh_get_BINVAL' defined but not used
+../fh_registry.h:913: warning: `fh_kw_BINVAL' defined but not used
+../fh_registry.h:913: warning: `fh_cmt_BINVAL' defined but not used
+../fh_registry.h:922: warning: `fh_set_GRFPPOS' defined but not used
+../fh_registry.h:922: warning: `fh_setval_GRFPPOS' defined but not used
+../fh_registry.h:922: warning: `fh_setcmt_GRFPPOS' defined but not used
+../fh_registry.h:922: warning: `fh_get_GRFPPOS' defined but not used
+../fh_registry.h:922: warning: `fh_kw_GRFPPOS' defined but not used
+../fh_registry.h:922: warning: `fh_cmt_GRFPPOS' defined but not used
+../fh_registry.h:927: warning: `fh_set_GRWAVORD' defined but not used
+../fh_registry.h:927: warning: `fh_setval_GRWAVORD' defined but not used
+../fh_registry.h:927: warning: `fh_setcmt_GRWAVORD' defined but not used
+../fh_registry.h:927: warning: `fh_get_GRWAVORD' defined but not used
+../fh_registry.h:927: warning: `fh_kw_GRWAVORD' defined but not used
+../fh_registry.h:927: warning: `fh_cmt_GRWAVORD' defined but not used
+../fh_registry.h:928: warning: `fh_set_GRWAVCAL' defined but not used
+../fh_registry.h:928: warning: `fh_setval_GRWAVCAL' defined but not used
+../fh_registry.h:928: warning: `fh_setcmt_GRWAVCAL' defined but not used
+../fh_registry.h:928: warning: `fh_get_GRWAVCAL' defined but not used
+../fh_registry.h:928: warning: `fh_kw_GRWAVCAL' defined but not used
+../fh_registry.h:928: warning: `fh_cmt_GRWAVCAL' defined but not used
+../fh_registry.h:929: warning: `fh_set_GRBCVCAL' defined but not used
+../fh_registry.h:929: warning: `fh_setval_GRBCVCAL' defined but not used
+../fh_registry.h:929: warning: `fh_setcmt_GRBCVCAL' defined but not used
+../fh_registry.h:929: warning: `fh_get_GRBCVCAL' defined but not used
+../fh_registry.h:929: warning: `fh_kw_GRBCVCAL' defined but not used
+../fh_registry.h:929: warning: `fh_cmt_GRBCVCAL' defined but not used
+../fh_registry.h:936: warning: `fh_set_GRSCANTY' defined but not used
+../fh_registry.h:936: warning: `fh_setval_GRSCANTY' defined but not used
+../fh_registry.h:936: warning: `fh_setcmt_GRSCANTY' defined but not used
+../fh_registry.h:936: warning: `fh_get_GRSCANTY' defined but not used
+../fh_registry.h:936: warning: `fh_kw_GRSCANTY' defined but not used
+../fh_registry.h:936: warning: `fh_cmt_GRSCANTY' defined but not used
+../fh_registry.h:940: warning: `fh_set_GRWAVBEG' defined but not used
+../fh_registry.h:940: warning: `fh_setval_GRWAVBEG' defined but not used
+../fh_registry.h:940: warning: `fh_setcmt_GRWAVBEG' defined but not used
+../fh_registry.h:940: warning: `fh_get_GRWAVBEG' defined but not used
+../fh_registry.h:940: warning: `fh_kw_GRWAVBEG' defined but not used
+../fh_registry.h:940: warning: `fh_cmt_GRWAVBEG' defined but not used
+../fh_registry.h:941: warning: `fh_set_GRWAVSTP' defined but not used
+../fh_registry.h:941: warning: `fh_setval_GRWAVSTP' defined but not used
+../fh_registry.h:941: warning: `fh_setcmt_GRWAVSTP' defined but not used
+../fh_registry.h:941: warning: `fh_get_GRWAVSTP' defined but not used
+../fh_registry.h:941: warning: `fh_kw_GRWAVSTP' defined but not used
+../fh_registry.h:941: warning: `fh_cmt_GRWAVSTP' defined but not used
+../fh_registry.h:942: warning: `fh_set_GRWAVCUR' defined but not used
+../fh_registry.h:942: warning: `fh_setval_GRWAVCUR' defined but not used
+../fh_registry.h:942: warning: `fh_setcmt_GRWAVCUR' defined but not used
+../fh_registry.h:942: warning: `fh_get_GRWAVCUR' defined but not used
+../fh_registry.h:942: warning: `fh_kw_GRWAVCUR' defined but not used
+../fh_registry.h:942: warning: `fh_cmt_GRWAVCUR' defined but not used
+../fh_registry.h:948: warning: `fh_set_GRBCVBEG' defined but not used
+../fh_registry.h:948: warning: `fh_setval_GRBCVBEG' defined but not used
+../fh_registry.h:948: warning: `fh_setcmt_GRBCVBEG' defined but not used
+../fh_registry.h:948: warning: `fh_get_GRBCVBEG' defined but not used
+../fh_registry.h:948: warning: `fh_kw_GRBCVBEG' defined but not used
+../fh_registry.h:948: warning: `fh_cmt_GRBCVBEG' defined but not used
+../fh_registry.h:949: warning: `fh_set_GRBCVSTP' defined but not used
+../fh_registry.h:949: warning: `fh_setval_GRBCVSTP' defined but not used
+../fh_registry.h:949: warning: `fh_setcmt_GRBCVSTP' defined but not used
+../fh_registry.h:949: warning: `fh_get_GRBCVSTP' defined but not used
+../fh_registry.h:949: warning: `fh_kw_GRBCVSTP' defined but not used
+../fh_registry.h:949: warning: `fh_cmt_GRBCVSTP' defined but not used
+../fh_registry.h:950: warning: `fh_set_GRBCVCUR' defined but not used
+../fh_registry.h:950: warning: `fh_setval_GRBCVCUR' defined but not used
+../fh_registry.h:950: warning: `fh_setcmt_GRBCVCUR' defined but not used
+../fh_registry.h:950: warning: `fh_get_GRBCVCUR' defined but not used
+../fh_registry.h:950: warning: `fh_kw_GRBCVCUR' defined but not used
+../fh_registry.h:950: warning: `fh_cmt_GRBCVCUR' defined but not used
+../fh_registry.h:956: warning: `fh_set_GRNBCHAN' defined but not used
+../fh_registry.h:956: warning: `fh_setval_GRNBCHAN' defined but not used
+../fh_registry.h:956: warning: `fh_setcmt_GRNBCHAN' defined but not used
+../fh_registry.h:956: warning: `fh_get_GRNBCHAN' defined but not used
+../fh_registry.h:956: warning: `fh_kw_GRNBCHAN' defined but not used
+../fh_registry.h:956: warning: `fh_cmt_GRNBCHAN' defined but not used
+../fh_registry.h:957: warning: `fh_set_GRCURRCH' defined but not used
+../fh_registry.h:957: warning: `fh_setval_GRCURRCH' defined but not used
+../fh_registry.h:957: warning: `fh_setcmt_GRCURRCH' defined but not used
+../fh_registry.h:957: warning: `fh_get_GRCURRCH' defined but not used
+../fh_registry.h:957: warning: `fh_kw_GRCURRCH' defined but not used
+../fh_registry.h:957: warning: `fh_cmt_GRCURRCH' defined but not used
+../fh_registry.h:962: warning: `fh_set_GRBCV_X' defined but not used
+../fh_registry.h:962: warning: `fh_setval_GRBCV_X' defined but not used
+../fh_registry.h:962: warning: `fh_setcmt_GRBCV_X' defined but not used
+../fh_registry.h:962: warning: `fh_get_GRBCV_X' defined but not used
+../fh_registry.h:962: warning: `fh_kw_GRBCV_X' defined but not used
+../fh_registry.h:962: warning: `fh_cmt_GRBCV_X' defined but not used
+../fh_registry.h:963: warning: `fh_set_GRBCV_Y' defined but not used
+../fh_registry.h:963: warning: `fh_setval_GRBCV_Y' defined but not used
+../fh_registry.h:963: warning: `fh_setcmt_GRBCV_Y' defined but not used
+../fh_registry.h:963: warning: `fh_get_GRBCV_Y' defined but not used
+../fh_registry.h:963: warning: `fh_kw_GRBCV_Y' defined but not used
+../fh_registry.h:963: warning: `fh_cmt_GRBCV_Y' defined but not used
+../fh_registry.h:975: warning: `fh_set_TESPMIRE' defined but not used
+../fh_registry.h:975: warning: `fh_setval_TESPMIRE' defined but not used
+../fh_registry.h:975: warning: `fh_setcmt_TESPMIRE' defined but not used
+../fh_registry.h:975: warning: `fh_get_TESPMIRE' defined but not used
+../fh_registry.h:975: warning: `fh_kw_TESPMIRE' defined but not used
+../fh_registry.h:975: warning: `fh_cmt_TESPMIRE' defined but not used
+../fh_registry.h:976: warning: `fh_set_TESPMIRW' defined but not used
+../fh_registry.h:976: warning: `fh_setval_TESPMIRW' defined but not used
+../fh_registry.h:976: warning: `fh_setcmt_TESPMIRW' defined but not used
+../fh_registry.h:976: warning: `fh_get_TESPMIRW' defined but not used
+../fh_registry.h:976: warning: `fh_kw_TESPMIRW' defined but not used
+../fh_registry.h:976: warning: `fh_cmt_TESPMIRW' defined but not used
+../fh_registry.h:977: warning: `fh_set_TESPMIRS' defined but not used
+../fh_registry.h:977: warning: `fh_setval_TESPMIRS' defined but not used
+../fh_registry.h:977: warning: `fh_setcmt_TESPMIRS' defined but not used
+../fh_registry.h:977: warning: `fh_get_TESPMIRS' defined but not used
+../fh_registry.h:977: warning: `fh_kw_TESPMIRS' defined but not used
+../fh_registry.h:977: warning: `fh_cmt_TESPMIRS' defined but not used
+../fh_registry.h:978: warning: `fh_set_TEAPMCLW' defined but not used
+../fh_registry.h:978: warning: `fh_setval_TEAPMCLW' defined but not used
+../fh_registry.h:978: warning: `fh_setcmt_TEAPMCLW' defined but not used
+../fh_registry.h:978: warning: `fh_get_TEAPMCLW' defined but not used
+../fh_registry.h:978: warning: `fh_kw_TEAPMCLW' defined but not used
+../fh_registry.h:978: warning: `fh_cmt_TEAPMCLW' defined but not used
+../fh_registry.h:979: warning: `fh_set_TEAMIRCI' defined but not used
+../fh_registry.h:979: warning: `fh_setval_TEAMIRCI' defined but not used
+../fh_registry.h:979: warning: `fh_setcmt_TEAMIRCI' defined but not used
+../fh_registry.h:979: warning: `fh_get_TEAMIRCI' defined but not used
+../fh_registry.h:979: warning: `fh_kw_TEAMIRCI' defined but not used
+../fh_registry.h:979: warning: `fh_cmt_TEAMIRCI' defined but not used
+../fh_registry.h:980: warning: `fh_set_TEAMIRCO' defined but not used
+../fh_registry.h:980: warning: `fh_setval_TEAMIRCO' defined but not used
+../fh_registry.h:980: warning: `fh_setcmt_TEAMIRCO' defined but not used
+../fh_registry.h:980: warning: `fh_get_TEAMIRCO' defined but not used
+../fh_registry.h:980: warning: `fh_kw_TEAMIRCO' defined but not used
+../fh_registry.h:980: warning: `fh_cmt_TEAMIRCO' defined but not used
+../fh_registry.h:981: warning: `fh_set_TEAPMSPN' defined but not used
+../fh_registry.h:981: warning: `fh_setval_TEAPMSPN' defined but not used
+../fh_registry.h:981: warning: `fh_setcmt_TEAPMSPN' defined but not used
+../fh_registry.h:981: warning: `fh_get_TEAPMSPN' defined but not used
+../fh_registry.h:981: warning: `fh_kw_TEAPMSPN' defined but not used
+../fh_registry.h:981: warning: `fh_cmt_TEAPMSPN' defined but not used
+../fh_registry.h:982: warning: `fh_set_TEAPMSPS' defined but not used
+../fh_registry.h:982: warning: `fh_setval_TEAPMSPS' defined but not used
+../fh_registry.h:982: warning: `fh_setcmt_TEAPMSPS' defined but not used
+../fh_registry.h:982: warning: `fh_get_TEAPMSPS' defined but not used
+../fh_registry.h:982: warning: `fh_kw_TEAPMSPS' defined but not used
+../fh_registry.h:982: warning: `fh_cmt_TEAPMSPS' defined but not used
+../fh_registry.h:983: warning: `fh_set_TEATRNGE' defined but not used
+../fh_registry.h:983: warning: `fh_setval_TEATRNGE' defined but not used
+../fh_registry.h:983: warning: `fh_setcmt_TEATRNGE' defined but not used
+../fh_registry.h:983: warning: `fh_get_TEATRNGE' defined but not used
+../fh_registry.h:983: warning: `fh_kw_TEATRNGE' defined but not used
+../fh_registry.h:983: warning: `fh_cmt_TEATRNGE' defined but not used
+../fh_registry.h:984: warning: `fh_set_TEATRNGW' defined but not used
+../fh_registry.h:984: warning: `fh_setval_TEATRNGW' defined but not used
+../fh_registry.h:984: warning: `fh_setcmt_TEATRNGW' defined but not used
+../fh_registry.h:984: warning: `fh_get_TEATRNGW' defined but not used
+../fh_registry.h:984: warning: `fh_kw_TEATRNGW' defined but not used
+../fh_registry.h:984: warning: `fh_cmt_TEATRNGW' defined but not used
+../fh_registry.h:985: warning: `fh_set_TEANRLSB' defined but not used
+../fh_registry.h:985: warning: `fh_setval_TEANRLSB' defined but not used
+../fh_registry.h:985: warning: `fh_setcmt_TEANRLSB' defined but not used
+../fh_registry.h:985: warning: `fh_get_TEANRLSB' defined but not used
+../fh_registry.h:985: warning: `fh_kw_TEANRLSB' defined but not used
+../fh_registry.h:985: warning: `fh_cmt_TEANRLSB' defined but not used
+../fh_registry.h:986: warning: `fh_set_TESHRSET' defined but not used
+../fh_registry.h:986: warning: `fh_setval_TESHRSET' defined but not used
+../fh_registry.h:986: warning: `fh_setcmt_TESHRSET' defined but not used
+../fh_registry.h:986: warning: `fh_get_TESHRSET' defined but not used
+../fh_registry.h:986: warning: `fh_kw_TESHRSET' defined but not used
+../fh_registry.h:986: warning: `fh_cmt_TESHRSET' defined but not used
+../fh_registry.h:987: warning: `fh_set_TESTELTL' defined but not used
+../fh_registry.h:987: warning: `fh_setval_TESTELTL' defined but not used
+../fh_registry.h:987: warning: `fh_setcmt_TESTELTL' defined but not used
+../fh_registry.h:987: warning: `fh_get_TESTELTL' defined but not used
+../fh_registry.h:987: warning: `fh_kw_TESTELTL' defined but not used
+../fh_registry.h:987: warning: `fh_cmt_TESTELTL' defined but not used
+../fh_registry.h:988: warning: `fh_set_TESTELTH' defined but not used
+../fh_registry.h:988: warning: `fh_setval_TESTELTH' defined but not used
+../fh_registry.h:988: warning: `fh_setcmt_TESTELTH' defined but not used
+../fh_registry.h:988: warning: `fh_get_TESTELTH' defined but not used
+../fh_registry.h:988: warning: `fh_kw_TESTELTH' defined but not used
+../fh_registry.h:988: warning: `fh_cmt_TESTELTH' defined but not used
+../fh_registry.h:989: warning: `fh_set_TEALOWWS' defined but not used
+../fh_registry.h:989: warning: `fh_setval_TEALOWWS' defined but not used
+../fh_registry.h:989: warning: `fh_setcmt_TEALOWWS' defined but not used
+../fh_registry.h:989: warning: `fh_get_TEALOWWS' defined but not used
+../fh_registry.h:989: warning: `fh_kw_TEALOWWS' defined but not used
+../fh_registry.h:989: warning: `fh_cmt_TEALOWWS' defined but not used
+../fh_registry.h:990: warning: `fh_set_TEATOPWS' defined but not used
+../fh_registry.h:990: warning: `fh_setval_TEATOPWS' defined but not used
+../fh_registry.h:990: warning: `fh_setcmt_TEATOPWS' defined but not used
+../fh_registry.h:990: warning: `fh_get_TEATOPWS' defined but not used
+../fh_registry.h:990: warning: `fh_kw_TEATOPWS' defined but not used
+../fh_registry.h:990: warning: `fh_cmt_TEATOPWS' defined but not used
+../fh_registry.h:991: warning: `fh_set_TEATOPOP' defined but not used
+../fh_registry.h:991: warning: `fh_setval_TEATOPOP' defined but not used
+../fh_registry.h:991: warning: `fh_setcmt_TEATOPOP' defined but not used
+../fh_registry.h:991: warning: `fh_get_TEATOPOP' defined but not used
+../fh_registry.h:991: warning: `fh_kw_TEATOPOP' defined but not used
+../fh_registry.h:991: warning: `fh_cmt_TEATOPOP' defined but not used
+../fh_registry.h:992: warning: `fh_set_TEA2INCH' defined but not used
+../fh_registry.h:992: warning: `fh_setval_TEA2INCH' defined but not used
+../fh_registry.h:992: warning: `fh_setcmt_TEA2INCH' defined but not used
+../fh_registry.h:992: warning: `fh_get_TEA2INCH' defined but not used
+../fh_registry.h:992: warning: `fh_kw_TEA2INCH' defined but not used
+../fh_registry.h:992: warning: `fh_cmt_TEA2INCH' defined but not used
+../fh_registry.h:993: warning: `fh_set_TEA2INEB' defined but not used
+../fh_registry.h:993: warning: `fh_setval_TEA2INEB' defined but not used
+../fh_registry.h:993: warning: `fh_setcmt_TEA2INEB' defined but not used
+../fh_registry.h:993: warning: `fh_get_TEA2INEB' defined but not used
+../fh_registry.h:993: warning: `fh_kw_TEA2INEB' defined but not used
+../fh_registry.h:993: warning: `fh_cmt_TEA2INEB' defined but not used
+../fh_registry.h:994: warning: `fh_set_TEA6FOOT' defined but not used
+../fh_registry.h:994: warning: `fh_setval_TEA6FOOT' defined but not used
+../fh_registry.h:994: warning: `fh_setcmt_TEA6FOOT' defined but not used
+../fh_registry.h:994: warning: `fh_get_TEA6FOOT' defined but not used
+../fh_registry.h:994: warning: `fh_kw_TEA6FOOT' defined but not used
+../fh_registry.h:994: warning: `fh_cmt_TEA6FOOT' defined but not used
+../fh_registry.h:995: warning: `fh_set_TESCONRM' defined but not used
+../fh_registry.h:995: warning: `fh_setval_TESCONRM' defined but not used
+../fh_registry.h:995: warning: `fh_setcmt_TESCONRM' defined but not used
+../fh_registry.h:995: warning: `fh_get_TESCONRM' defined but not used
+../fh_registry.h:995: warning: `fh_kw_TESCONRM' defined but not used
+../fh_registry.h:995: warning: `fh_cmt_TESCONRM' defined but not used
+../fh_registry.h:996: warning: `fh_set_TESPIERN' defined but not used
+../fh_registry.h:996: warning: `fh_setval_TESPIERN' defined but not used
+../fh_registry.h:996: warning: `fh_setcmt_TESPIERN' defined but not used
+../fh_registry.h:996: warning: `fh_get_TESPIERN' defined but not used
+../fh_registry.h:996: warning: `fh_kw_TESPIERN' defined but not used
+../fh_registry.h:996: warning: `fh_cmt_TESPIERN' defined but not used
+../fh_registry.h:997: warning: `fh_set_TESPIERS' defined but not used
+../fh_registry.h:997: warning: `fh_setval_TESPIERS' defined but not used
+../fh_registry.h:997: warning: `fh_setcmt_TESPIERS' defined but not used
+../fh_registry.h:997: warning: `fh_get_TESPIERS' defined but not used
+../fh_registry.h:997: warning: `fh_kw_TESPIERS' defined but not used
+../fh_registry.h:997: warning: `fh_cmt_TESPIERS' defined but not used
+../fh_registry.h:998: warning: `fh_set_TEAWTHRT' defined but not used
+../fh_registry.h:998: warning: `fh_setval_TEAWTHRT' defined but not used
+../fh_registry.h:998: warning: `fh_setcmt_TEAWTHRT' defined but not used
+../fh_registry.h:998: warning: `fh_get_TEAWTHRT' defined but not used
+../fh_registry.h:998: warning: `fh_kw_TEAWTHRT' defined but not used
+../fh_registry.h:998: warning: `fh_cmt_TEAWTHRT' defined but not used
+../fh_registry.h:999: warning: `fh_set_TEMPERAT' defined but not used
+../fh_registry.h:999: warning: `fh_setval_TEMPERAT' defined but not used
+../fh_registry.h:999: warning: `fh_setcmt_TEMPERAT' defined but not used
+../fh_registry.h:999: warning: `fh_get_TEMPERAT' defined but not used
+../fh_registry.h:999: warning: `fh_kw_TEMPERAT' defined but not used
+../fh_registry.h:999: warning: `fh_cmt_TEMPERAT' defined but not used
+../fh_registry.h:1000: warning: `fh_set_WINDSPED' defined but not used
+../fh_registry.h:1000: warning: `fh_setval_WINDSPED' defined but not used
+../fh_registry.h:1000: warning: `fh_setcmt_WINDSPED' defined but not used
+../fh_registry.h:1000: warning: `fh_get_WINDSPED' defined but not used
+../fh_registry.h:1000: warning: `fh_kw_WINDSPED' defined but not used
+../fh_registry.h:1000: warning: `fh_cmt_WINDSPED' defined but not used
+../fh_registry.h:1001: warning: `fh_set_WINDDIR' defined but not used
+../fh_registry.h:1001: warning: `fh_setval_WINDDIR' defined but not used
+../fh_registry.h:1001: warning: `fh_setcmt_WINDDIR' defined but not used
+../fh_registry.h:1001: warning: `fh_get_WINDDIR' defined but not used
+../fh_registry.h:1001: warning: `fh_kw_WINDDIR' defined but not used
+../fh_registry.h:1001: warning: `fh_cmt_WINDDIR' defined but not used
+../fh_registry.h:1002: warning: `fh_set_RELHUMID' defined but not used
+../fh_registry.h:1002: warning: `fh_setval_RELHUMID' defined but not used
+../fh_registry.h:1002: warning: `fh_setcmt_RELHUMID' defined but not used
+../fh_registry.h:1002: warning: `fh_get_RELHUMID' defined but not used
+../fh_registry.h:1002: warning: `fh_kw_RELHUMID' defined but not used
+../fh_registry.h:1002: warning: `fh_cmt_RELHUMID' defined but not used
+../fh_registry.h:1003: warning: `fh_set_PRESSURE' defined but not used
+../fh_registry.h:1003: warning: `fh_setval_PRESSURE' defined but not used
+../fh_registry.h:1003: warning: `fh_setcmt_PRESSURE' defined but not used
+../fh_registry.h:1003: warning: `fh_get_PRESSURE' defined but not used
+../fh_registry.h:1003: warning: `fh_kw_PRESSURE' defined but not used
+../fh_registry.h:1003: warning: `fh_cmt_PRESSURE' defined but not used
+../fh_registry.h:1015: warning: `fh_set_CMTPROC1' defined but not used
+../fh_registry.h:1016: warning: `fh_set_CMTPROC2' defined but not used
+../fh_registry.h:1017: warning: `fh_set_CMTPROC3' defined but not used
+../fh_registry.h:1018: warning: `fh_set_CMTPROC4' defined but not used
+../fh_registry.h:1019: warning: `fh_set_CRUNID' defined but not used
+../fh_registry.h:1019: warning: `fh_setval_CRUNID' defined but not used
+../fh_registry.h:1019: warning: `fh_setcmt_CRUNID' defined but not used
+../fh_registry.h:1019: warning: `fh_get_CRUNID' defined but not used
+../fh_registry.h:1019: warning: `fh_kw_CRUNID' defined but not used
+../fh_registry.h:1019: warning: `fh_cmt_CRUNID' defined but not used
Index: /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfh/tests/t.sh
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfh/tests/t.sh	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfh/tests/t.sh	(revision 23594)
@@ -0,0 +1,7 @@
+#!/apps/gnu/bin/bash
+#
+# test compile current fh_registry.h
+
+gcc -Wall t.c -c -o t.o >t.log 2>&1
+grep -v "defined but not" t.log
+rm -f t.o
Index: /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfhreg/Index
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfhreg/Index	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfhreg/Index	(revision 23594)
@@ -0,0 +1,21 @@
+# Description:
+
+  package		FITS Keyword Registry
+  version		0
+  organization		Pan-STARRS
+  email			isani@ifa.hawaii.edu
+  year			2004
+
+# Contents:
+
+  Makefile		Use with GNU make to build libfhreg
+
+  dummy.c		Placeholder.  There is no C code with libfhreg now.
+  macros.h		CPP macros to make fhreg work, automatically included
+  general.h		General FITS structure keyword registry entries
+  gpc_all.h		Top-level include file that includes all categories
+  gpc_detector.h	Pan-STARRS Giga Pixel Camera detector keywords
+  gpc_instrument.h	Pan-STARRS instrument FITS header keywords
+  gpc_shutter.h	        Pan-STARRS shutter FITS header keywords
+  gpc_telescope.h	Pan-STARRS TCS FITS header keywords
+  gpc_plant.h		Pan-STARRS plant-related FITS header keywords
Index: /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfhreg/Make.Common
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfhreg/Make.Common	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfhreg/Make.Common	(revision 23594)
@@ -0,0 +1,1 @@
+link ../Make.Common
Index: /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfhreg/Makefile
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfhreg/Makefile	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfhreg/Makefile	(revision 23594)
@@ -0,0 +1,12 @@
+# `Makefile' - Use with GNU make to build libfhreg
+#
+#   This file is part of version 0 of the FITS Keyword Registry.
+#   Read the `License' file for terms of use and distribution.
+#   Copyright 2004, Pan-STARRS, isani@ifa.hawaii.edu.
+#
+# ___This header automatically generated from `Index'. Do not edit it here!___
+include ../Make.Common
+include ../Make.Common
+# Dependencies by Make.Common $Revision: 2.17 $
+
+$(OBJ)/dummy.o: dummy.c
Index: /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfhreg/dummy.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfhreg/dummy.c	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfhreg/dummy.c	(revision 23594)
@@ -0,0 +1,9 @@
+/*                                             -*- c-file-style: "Ellemtel" -*-
+
+`dummy.c' - Placeholder.  There is no C code with libfhreg now.
+
+This file is part of version 0 of the FITS Keyword Registry.
+Read the `License' file for terms of use and distribution.
+Copyright 2004, Pan-STARRS, isani@ifa.hawaii.edu.
+
+___This header automatically generated from `Index'. Do not edit it here!___ */
Index: /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfhreg/general.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfhreg/general.h	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfhreg/general.h	(revision 23594)
@@ -0,0 +1,131 @@
+/*                                             -*- c-file-style: "Ellemtel" -*-
+
+`general.h' - General FITS structure keyword registry entries
+
+This file is part of version 0 of the FITS Keyword Registry.
+Read the `License' file for terms of use and distribution.
+Copyright 2004, Pan-STARRS, isani@ifa.hawaii.edu.
+
+___This header automatically generated from `Index'. Do not edit it here!___ */
+
+#include "macros.h"
+
+/*
+ * Description of columns:
+ *
+ *      TYPE - MK_BLN, MK_STR, MK_INT, MK_FLT, MK_PFL selects a type for the
+ *             keyword (True/False, 'String  ', integer, or floating point.)
+ *             Any keyword can still be set to any type with fh_setval_*(),
+ *             which takes a pre-formatted character string for the card.
+ *
+ *      IDX  - Index sorting number, which eventually determines the final
+ *             order of keywords in the FITS header.  These are floating point
+ *             values so new keywords can always be inserted in between
+ *             without shifting all the values.
+ *
+ *   KEYWORD - The name of the keyword.  Macros will be generated of the form
+ *             fh_get_KEYWORD(), fh_set_KEYWORD(), and fh_setval_KEYWORD().
+ *
+ *      PRC  - For MK_FLT(), this specifies the (scientific) precision,
+ *             or number of significant digits in the value.  If necessary,
+ *             scientific notation will be used to format the value (see
+ *             rules for printf "%.*G").
+ *             For MK_PFL(), this specifies mathematical precision, or number
+ *             of decimal places in the formatted value (see the rules for
+ *             printf "%.*f").
+ *
+ *             Use MK_PFL() (make "precise float") if that value is always
+ *             precise to an exact number digits after the decimal point.
+ *             Use MK_FLT() if the value might require scientific notation.
+ *             All results are FITS-standard legal.
+ *
+ *   COMMENT - String to be included after the / in the FITS card, if
+ *             there is room within the 80 columns (it may get truncated.)
+ *             Note that in the text below, 80 columns are reached when
+ *             there is still one space between the " and the ) at the end
+ *             of the line.
+ *
+ * FITS Structure : 0.000 to 9.999
+ *
+ * The following keywords give basic information about the structure and
+ * dimensions of the FITS file, and must appear in the specific order
+ * below, usually without any other intervening keywords.
+ *
+ *TYPE --IDX--  KEYWORD- PRC -------------------COMMENT----------------------*/
+MK_BLN( 0.0,	SIMPLE  ,   "Standard FITS"                                   )
+MK_STR( 0.0,	XTENSION,   ""                                                )
+MK_INT( 1.0,	BITPIX  ,   "Bits per pixel"                                  )
+MK_INT( 2.0,	NAXIS   ,   "Number of axes"                                  )
+MK_INT( 2.1,	NAXIS1  ,   "Number of pixel columns"                         )
+MK_INT( 2.2,	NAXIS2  ,   "Number of pixel rows"                            )
+MK_INT( 2.3,	NAXIS3  ,   "Number of stacked frames (cube)"                 )
+MK_BLN( 3.0,	EXTEND  ,   "File contains extensions"                        )
+MK_INT( 3.1,	NEXTEND ,   "Number of extensions"                            )
+MK_BLN( 4.0,	GROUPS  ,   "File contains random groups records"             )
+MK_INT( 5.0,	PCOUNT  ,   "Random parameters before each array in a group"  )
+MK_INT( 6.0,	GCOUNT  ,   "Number of random groups"                         )
+MK_INT( 7.0000,	TFIELDS ,   "Number of fields in a row"                       )
+MK_STR( 7.0011,	TFORM1  ,   "Table format for field 1"                        )
+MK_INT( 7.0012,	TBCOL1  ,   "Start Column for field 1"                        )
+MK_STR( 7.0021,	TFORM2  ,   "Table format for field 2"                        )
+MK_INT( 7.0022,	TBCOL2  ,   "Start Column for field 2"                        )
+MK_STR( 7.0031,	TFORM3  ,   "Table format for field 3"                        )
+MK_INT( 7.0032,	TBCOL3  ,   "Start Column for field 3"                        )
+MK_STR( 7.0041,	TFORM4  ,   "Table format for field 4"                        )
+MK_INT( 7.0042,	TBCOL4  ,   "Start Column for field 4"                        )
+MK_STR( 7.0051,	TFORM5  ,   "Table format for field 5"                        )
+MK_INT( 7.0052,	TBCOL5  ,   "Start Column for field 5"                        )
+MK_BLN( 9.0,	INHERIT	,   "Inherit global keywords from primary header"     )
+MK_PFL( 10.0,   BZERO   ,1, "Zero factor"                                     )
+MK_PFL( 11.0,	BSCALE  ,1, "Scale factor"                                    )
+
+/*
+ * Summary : 50.000 to 59.999
+ *
+ *TYPE --IDX--  KEYWORD- PRC -------------------COMMENT----------------------*/
+MK_CMT(   50.1, CMTSUM1 ,   ""                                                )
+MK_CMT(   50.2, CMTSUM2 ,   "Observation Summary"                             )
+MK_CMT(   50.3, CMTSUM3 ,   "-------------------"                             )
+MK_CMT(   50.4, CMTSUM4 ,   ""                                                )
+MK_CMT(   50.5, CMTSUM5 ,   ""                                                )
+MK_STR(   51.0, CMMTOBS ,   ""                                                )
+MK_STR(   52.0, CMMTSEQ ,   ""                                                )
+MK_STR(   53.0, OBJECT  ,   ""                                                )
+MK_STR(   54.0, OBSERVER,   ""                                                )
+MK_STR(   55.0, PI_NAME ,   ""                                                )
+MK_STR(   56.0, RUNID   ,   ""                                                )
+MK_STR(   57.0, QUEUEID ,   ""                                                )
+MK_STR(   58.0, OBS_MODE,   ""                                                )
+MK_INT(   59.0, TESSEL  ,   ""                                                )
+
+/*
+ * General : 70.000 to 79.999
+ *
+ *TYPE --IDX--  KEYWORD- PRC -------------------COMMENT----------------------*/
+MK_CMT(  70.10, CMTGEN1 ,   ""                                                )
+MK_CMT(  70.20, CMTGEN2 ,   "General"                                         )
+MK_CMT(  70.30, CMTGEN3 ,   "-------"                                         )
+MK_CMT(  70.40, CMTGEN4 ,   ""                                                )
+MK_STR(  71.00, FILENAME,   "Base filename at acquisition"                    )
+MK_STR(  71.01, PATHNAME,   "Original directory name at acquisition"          )
+MK_STR(  71.10, EXTNAME ,   "Extension name"                                  )
+MK_INT(  71.20, EXTVER  ,   "Extension version"                               )
+MK_INT(  71.21, IMAGEID ,   ""                                                )
+MK_STR(  74.00, DATE    ,   "UTC Date of file creation"                       )
+MK_STR(  74.10, HSTTIME ,   "Local time in Hawaii"                            )
+MK_STR(  76.99, IMGSWPRG,   "Image creation software program"                 )
+MK_STR(  77.00, IMAGESWV,   "Image creation software version"                 )
+MK_STR(  79.00, ORIGIN  ,   ""                                                )
+
+/*
+ * Summary : 90.0 Key stuff from instrument, detector (and other?) sections
+ */
+MK_STR(   91.0, INSTRUME,   "Instrument Name"                                 )
+MK_STR(   91.1, INSTMODE,   "Instrument Mode"                                 )
+MK_STR(   92.0,	DETECTOR,   "Science Detector"                                )
+MK_STR(   92.1, FPPOS,      "Position of detector in focal plane mosaic"      )
+
+MK_STR(   95.0, OBSTYPE,    "Observation/Exposure type"                       )
+MK_PFL(   96.0, EXPTIME,3,  "Exposure time (seconds)"                         )
+MK_PFL(   96.1, EXPREQ ,3,  "Exposure time requested (seconds)"               )
+MK_PFL(   97.0, DARKTIME,3, "Dark current time (seconds)"                     )
Index: /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfhreg/gpc_all.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfhreg/gpc_all.h	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfhreg/gpc_all.h	(revision 23594)
@@ -0,0 +1,21 @@
+/*                                             -*- c-file-style: "Ellemtel" -*-
+
+`gpc_all.h' - Top-level include file that includes all categories
+
+This file is part of version 0 of the FITS Keyword Registry.
+Read the `License' file for terms of use and distribution.
+Copyright 2004, Pan-STARRS, isani@ifa.hawaii.edu.
+
+___This header automatically generated from `Index'. Do not edit it here!___ */
+
+/*
+ * Each subsystem registers their keywords in a separate file.
+ * Including gpc_all.h includes them all.
+ */
+#include "general.h"
+#include "gpc_detector.h"
+#include "gpc_shutter.h"
+#include "gpc_telescope.h"
+#include "gpc_instrument.h"
+#include "gpc_wcs.h"
+#include "gpc_plant.h"
Index: /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfhreg/gpc_detector.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfhreg/gpc_detector.h	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfhreg/gpc_detector.h	(revision 23594)
@@ -0,0 +1,215 @@
+/*                                             -*- c-file-style: "Ellemtel" -*-
+
+`gpc_detector.h' - Pan-STARRS Giga Pixel Camera detector keywords
+
+This file is part of version 0 of the FITS Keyword Registry.
+Read the `License' file for terms of use and distribution.
+Copyright 2004, Pan-STARRS, isani@ifa.hawaii.edu.
+
+___This header automatically generated from `Index'. Do not edit it here!___ */
+
+#include "macros.h"
+
+/*
+ * Description of columns:
+ *
+ *      TYPE - MK_BLN, MK_STR, MK_INT, MK_FLT, MK_PFL selects a type for the
+ *             keyword (True/False, 'String  ', integer, or floating point.)
+ *             Any keyword can still be set to any type with fh_setval_*(),
+ *             which takes a pre-formatted character string for the card.
+ *
+ *      IDX  - Index sorting number, which eventually determines the final
+ *             order of keywords in the FITS header.  These are floating point
+ *             values so new keywords can always be inserted in between
+ *             without shifting all the values.
+ *
+ *   KEYWORD - The name of the keyword.  Macros will be generated of the form
+ *             fh_get_KEYWORD(), fh_set_KEYWORD(), and fh_setval_KEYWORD().
+ *
+ *      PRC  - For MK_FLT(), this specifies the (scientific) precision,
+ *             or number of significant digits in the value.  If necessary,
+ *             scientific notation will be used to format the value (see
+ *             rules for printf "%.*G").
+ *             For MK_PFL(), this specifies mathematical precision, or number
+ *             of decimal places in the formatted value (see the rules for
+ *             printf "%.*f").
+ *
+ *             Use MK_PFL() (make "precise float") if that value is always
+ *             precise to an exact number digits after the decimal point.
+ *             Use MK_FLT() if the value might require scientific notation.
+ *             All results are FITS-standard legal.
+ *
+ *   COMMENT - String to be included after the / in the FITS card, if
+ *             there is room within the 80 columns (it may get truncated.)
+ *             Note that in the text below, 80 columns are reached when
+ *             there is still one space between the " and the ) at the end
+ *             of the line.
+ *
+ * FITS Structure : 0.000 to 9.999
+ *
+ * The following keywords give basic information about the structure and
+ * dimensions of the FITS file, and must appear in the specific order
+ * below, usually without any other intervening keywords.
+ *
+ * Detector : 100.000 to 199.999
+ *
+ *TYPE --IDX--  KEYWORD- PRC -------------------COMMENT----------------------*/
+MK_CMT(  100.1,	CMTDET1	,   ""                                                )
+MK_CMT(  100.2,	CMTDET2	,   "Detector"                                        )
+MK_CMT(  100.3,	CMTDET3	,   "--------"                                        )
+MK_CMT(  100.4,	CMTDET4	,   ""                                                )
+MK_STR(  103.0,	RASTER	,   ""                                                )
+MK_INT(  105.0, NAMPS   ,   "Number of amplifiers in the detector"            )
+MK_STR(  105.1,	AMPLIST	,   "List of amplifiers for this image"               )
+MK_STR(  105.2,	AMPNAME	,   "Amplifier name"                                  )
+MK_STR(  150.1, OTADEV  ,   "Identification of device type"                   )
+MK_STR(  150.1, CCDNAME,    "Name of the CCD (manufacturer reference)"        )
+MK_STR(  150.2, CCDNICK,    "Nickname of the CCD"                             )
+MK_INT(  152.0, MAXLIN  ,   "[ADU] Maximum linearity estimate"                )
+MK_INT(  152.1, SATURATE,   "[ADU] Saturation value"                          )
+MK_FLT(  155.0,	GAIN    ,3, "[e/ADU] est. gain from xray"                     )
+MK_FLT(  155.9, XRNOISE, 2, "[ADU] RMS noise in overscan on xray"             )
+MK_FLT(  156.0,	RDNOISE ,2, "[ADU] RMS Read noise on overscan"                )
+MK_FLT(  157.0, DCURRENT,5, "[ADU/pixel/sec] Dark current"                    )
+MK_FLT(  157.1, DARKCUR ,5, "[e-/pixel/hour] Dark current"                    )
+MK_STR(  158.0,	QEPOINTS,   "QE%@wavelength in nm"                            )
+MK_INT(  159.0, BIASLVL,    "[ADU] Bias level (overscan mean)"                )
+MK_INT(  159.2, BACKEST,    "[ADU] Background level est., overscan corrected" )
+MK_STR(  165.0,	DETSTAT	,   "Detector status"                                 )
+MK_FLT(  166.0, DETTEM  ,3, "[C] Detector temperature"                        )
+MK_PFL(  170.0,	PIXSIZE ,3, "[um] Pixel size for both axes"                   )
+MK_PFL(  171.1,	PIXSIZE1,3, "[um] Pixel size for axis 1"                      )
+MK_PFL(  171.2,	PIXSIZE2,3, "[um] Pixel size for axis 2"                      )
+MK_FLT(  172.1,	PIXSCAL1,4, "[arcsec/pixel] Pixel scale for axis 1"           )
+MK_FLT(  172.2,	PIXSCAL2,4, "[arcsec/pixel] Pixel scale for axis 2"           )
+MK_STR(  176.0, CELLMODE,   " 64 cells: S)cience D)ead V)ideo F)loat"         )
+MK_INT(  180.01,IMNAXIS1,   "OTA image width in unbinned pixels"              )
+MK_INT(  180.02,IMNAXIS2,   "OTA image height in unbinned pixels"             )
+MK_INT(  180.11,IMNPIX1 ,   "OTA image coordinate of amp. (the cell origin)"  )
+MK_INT(  180.12,IMNPIX2 ,   "OTA image coordinate of amp. (the cell origin)"  )
+MK_CMT(  181.01,CMTCEL1,
+         "The following keywords apply equally to each cell, so can be in PHU")
+MK_CMT(  181.02,CMTCEL2,
+         "If not, they MUST be included in every image extension."            )
+MK_PFL(  181.11,IMTM1_1 ,8, "IMage Transform Matrix; A unit vector in the"    )
+MK_PFL(  181.12,IMTM1_2 ,8, " serial (fast) direction of amplifier readout"   )
+MK_PFL(  181.21,IMTM2_1 ,8, "And a unit vector in parallel (slow) direction"  )
+MK_PFL(  181.22,IMTM2_2 ,8, " of amplifier readout"                           )
+MK_INT(  184.01,CELLGAP1,   "Cell gap between columns (in unbinned pixels)"   )
+MK_INT(  184.02,CELLGAP2,   "Cell gap between rows (in unbinned pixels)"      )
+MK_INT(  184.11,CNAXIS1 ,   "Cell imaging size in unbinned pixel columns"     )
+MK_INT(  184.12,CNAXIS2 ,   "Cell imaging size in unbinned pixel rows"        )
+MK_INT(  184.21,CNPIX1  ,   "Offset in unbinned pixel cols to readout start"  )
+MK_INT(  184.22,CNPIX2  ,   "Offset in unbinned pixel rows to readout start"  )
+MK_INT(  185.1,	CCDBIN1	,   "Binning factor along axis 1"                     )
+MK_INT(  185.2,	CCDBIN2	,   "Binning factor along axis 2"                     )
+MK_INT(  186.1, PRESCAN1,   "Prescan count on axis 1"                         )
+MK_INT(  186.2, PRESCAN2,   "Prescan count on axis 2"                         )
+MK_INT(  187.1, OVRSCAN1,   "Overscan count on axis 1"                        )
+MK_INT(  187.2, OVRSCAN2,   "Overscan count on axis 2"                        )
+MK_CMT(  195.01,CMTIRAF1,   ""                                                )
+MK_CMT(  195.02,CMTIRAF2,   "  Iraf coordinates"                              )
+MK_CMT(  195.03,CMTIRAF3,   "  ----------------"                              )
+MK_STR(  195.1, CCDSUM  ,   "Binning factors"                                 )
+MK_STR(  195.2,	DATASEC ,   "Imaging area of the detector"                    )
+MK_STR(  195.3,	BIASSEC ,   "Overscan (bias) area"                            )
+MK_STR(  195.4,	CCDSIZE ,   "Detector imaging area size"                      )
+MK_STR(  195.5, CCDSEC  ,   "Unbinned mapping of read section to CCD"         )
+MK_STR(  195.6, DETSIZE	,   "Iraf total image pixels in full mosaic"          )
+MK_STR(  195.7,	DETSEC  ,   "Iraf mosaic area of the detector"                )
+MK_PFL(  196.01,LTV1    ,8, "Iraf image transformation vector"                )
+MK_PFL(  196.02,LTV2    ,8, "Iraf image transformation vector"                )
+MK_PFL(  196.11,LTM1_1  ,8, "Iraf image transformation matrix"                )
+MK_PFL(  196.12,LTM1_2  ,8, "Iraf image transformation matrix"                )
+MK_PFL(  196.21,LTM2_1  ,8, "Iraf image transformation matrix"                )
+MK_PFL(  196.22,LTM2_2  ,8, "Iraf image transformation matrix"                )
+MK_PFL(  197.01,ATV1    ,8, "Iraf amplifier transformation vector"            )
+MK_PFL(  197.02,ATV2    ,8, "Iraf amplifier transformation vector"            )
+MK_PFL(  197.11,ATM1_1  ,8, "Iraf amplifier transformation matrix"            )
+MK_PFL(  197.12,ATM1_2  ,8, "Iraf amplifier transformation matrix"            )
+MK_PFL(  197.21,ATM2_1  ,8, "Iraf amplifier transformation matrix"            )
+MK_PFL(  197.22,ATM2_2  ,8, "Iraf amplifier transformation matrix"            )
+MK_CMT(  197.96,CMTCON1,    ""                                                )
+MK_CMT(  197.97,CMTCON2,    "  Controller Data"                               )
+MK_CMT(  197.98,CMTCON3,    "  ---------------"                               )
+MK_STR(  198.00,CONTROLR,   "Detector Controller"                             )
+MK_STR(  198.01,CONHWURL,   "Controller hardware SVN location"                )
+MK_STR(  198.02,CONHWV,     "Controller hardware SVN revision"                )
+MK_STR(  198.01,CONSWURL,   "Controller software SVN location"                )
+MK_STR(  198.02,CONSWV,     "Controller software SVN revision"                )
+MK_STR(  198.03,CON_MAC,    "Controller MAC (HW Ethernet) Address"            )
+MK_STR(  198.04,CON_IP,     "Controller IP Address"                           )
+MK_STR(  198.05,CON_NAME,   "Controller IP Hostname"                          )
+MK_INT(  198.06,CON_DEV,    "Controller device number (0 or 1)"               )
+MK_INT(  198.07,CON_UP,     "[seconds] Controller up-time"                    )
+MK_STR(  198.08,CON_VSUM,   "Device operating point md5sum"                   )
+MK_INT(  198.40,TSP_SHOP,   "Milliseconds from last clean to shutter open"    )
+MK_INT(  198.41,TSP_SHCL,   "Milliseconds from last clean to shutter close"   )
+MK_INT(  198.50,TSP_RD00,   "Milliseconds darktime for buffer (cell row) 0"   )
+MK_INT(  198.51,TSP_RD01,   "Milliseconds darktime for buffer (cell row) 1"   )
+MK_INT(  198.52,TSP_RD02,   "Milliseconds darktime for buffer (cell row) 2"   )
+MK_INT(  198.53,TSP_RD03,   "Milliseconds darktime for buffer (cell row) 3"   )
+MK_INT(  198.54,TSP_RD04,   "Milliseconds darktime for buffer (cell row) 4"   )
+MK_INT(  198.55,TSP_RD05,   "Milliseconds darktime for buffer (cell row) 5"   )
+MK_INT(  198.56,TSP_RD06,   "Milliseconds darktime for buffer (cell row) 6"   )
+MK_INT(  198.57,TSP_RD07,   "Milliseconds darktime for buffer (cell row) 7"   )
+MK_INT(  198.60,TSP_NOW,    "Milliseconds from last clean until save"         )
+MK_STR(  199.01,CLV_PPG4,   "Coded parallel timings"                          )
+MK_STR(  199.02,CLV_PG3,    "Coded serial timings"                            )
+MK_STR(  199.03,CLV_PG4,    "Coded SW/RST/VCLAMP/SAMP"                        )
+MK_STR(  199.04,CLV_ADC,    "Coded ADC parameters"                            )
+MK_INT(  199.05,CLV_TRIG,   "Cross-trigger phase delay (x 10nsec)"            )
+MK_INT(  199.06,CLV_PRES,   "Serial prescan, (skipped in addition to CNPIX1)" )
+MK_INT(  199.07,CLV_PIPE,   "Clocking pattern pipeline (in addition to ADC)"  )
+MK_INT(  199.071,CLV_PXTP,  "OT Pixel Type 1 or 2 (or 0 for normal CCD)"      )
+MK_STR(  199.072,ADC_CONF,  "ADC configuration"                               )
+MK_STR(  199.073,ADC_MUX,   "ADC MUX configuration"                           )
+MK_STR(  199.074,ADC_PGAR,  "Red ADC pga (adcgain val)"                       )
+MK_STR(  199.075,ADC_PGAG,  "Grn ADC pga (adcgain val)"                       )
+MK_STR(  199.076,ADC_PGAB,  "Blu ADC pga (adcgain val)"                       )
+MK_STR(  199.077,ADC_OFSR,  "Red ADC offset"                                  )
+MK_STR(  199.078,ADC_OFSG,  "Grn ADC offset"                                  )
+MK_STR(  199.079,ADC_OFSB,  "Blu ADC offset"                                  )
+MK_INT(  199.08,DAC_INIT,   "[mV] DAC internal vref offset voltage"           )
+MK_PFL(  199.09,DAC_S_L,3,  "[V] Serial and summing well LO"                  )
+MK_PFL(  199.10,DAC_S_H,3,  "[V] Serial and summing well HI"                  )
+MK_PFL(  199.11,DAC_SW_L,3, "[V] Summing Well LO           "                  )
+MK_PFL(  199.12,DAC_SW_H,3, "[V] Summing Well HI           "                  )
+MK_PFL(  199.13,DAC_S1_L,3, "[V] Serial 1 LO               "                  )
+MK_PFL(  199.14,DAC_S1_H,3, "[V] Serial 1 HI               "                  )
+MK_PFL(  199.15,DAC_S2_L,3, "[V] Serial 2 LO               "                  )
+MK_PFL(  199.16,DAC_S2_H,3, "[V] Serial 2 HI               "                  )
+MK_PFL(  199.17,DAC_S3_L,3, "[V] Serial 3 LO               "                  )
+MK_PFL(  199.18,DAC_S3_H,3, "[V] Serial 3 HI               "                  )
+MK_PFL(  199.19,DAC_P_L,3,  "[V] Parallel and P. standby LO"                  )
+MK_PFL(  199.20,DAC_P_H,3,  "[V] Parallel and P. standby HI"                  )
+MK_PFL(  199.21,DAC_P1_L,3, "[V] Parallel 1 LO             "                  )
+MK_PFL(  199.22,DAC_P1_H,3, "[V] Parallel 1 HI             "                  )
+MK_PFL(  199.23,DAC_P2_L,3, "[V] Parallel 2 LO             "                  )
+MK_PFL(  199.24,DAC_P2_H,3, "[V] Parallel 2 HI             "                  )
+MK_PFL(  199.25,DAC_P3_L,3, "[V] Parallel 3 LO             "                  )
+MK_PFL(  199.26,DAC_P3_H,3, "[V] Parallel 3 HI             "                  )
+MK_PFL(  199.27,DAC_P4_L,3, "[V] Parallel 4 LO             "                  )
+MK_PFL(  199.28,DAC_P4_H,3, "[V] Parallel 4 HI             "                  )
+MK_PFL(  199.29,DAC_PSL,3,  "[V] Parallel Standby LO       "                  )
+MK_PFL(  199.30,DAC_PSH,3,  "[V] Parallel Standby HI       "                  )
+MK_PFL(  199.31,DAC_RG_L,3, "[V] Reset Gate LO             "                  )
+MK_PFL(  199.32,DAC_RG_H,3, "[V] Reset Gate HI             "                  )
+MK_PFL(  199.33,DAC_SO,3,   "[V] 1st Stage Source          "                  )
+MK_PFL(  199.34,DAC_DR,3,   "[V] Drain Bias                "                  )
+MK_PFL(  199.35,DAC_VSS,3,  "[V] Logic Low Supply          "                  )
+MK_PFL(  199.36,DAC_VDDL,3, "[V] Logic Drain Supply LO     "                  )
+MK_PFL(  199.37,DAC_VDDH,3, "[V] Logic Drain Supply HI     "                  )
+MK_PFL(  199.38,DAC_OG1,3,  "[V] Output Gate Bias          "                  )
+MK_PFL(  199.39,DAC_RD,3,   "[V] Reset Drain Bias          "                  )
+MK_PFL(  199.40,DAC_SUB,3,  "[V] Substrate                 "                  )
+MK_PFL(  199.41,DAC_SCP,3,  "[V] Scupper Bias              "                  )
+MK_PFL(  199.42,DAC_LREF,3, "[V] Logic Reference Ground    "                  )
+MK_STR(  199.70,DAC_CAL0,   "[mV] Pedestal cal"                               )
+MK_STR(  199.71,DAC_CAL1,   "[mV] Pedestal cal"                               )
+MK_STR(  199.72,DAC_CAL2,   "[mV] Pedestal cal"                               )
+MK_STR(  199.73,DAC_CAL3,   "[mV] Pedestal cal"                               )
+MK_STR(  199.74,DAC_CAL4,   "[mV] Pedestal cal"                               )
+MK_STR(  199.75,DAC_CAL5,   "[mV] Pedestal cal"                               )
+MK_STR(  199.76,DAC_CAL6,   "[mV] Pedestal cal"                               )
+MK_STR(  199.77,DAC_CAL7,   "[mV] Pedestal cal"                               )
Index: /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfhreg/gpc_instrument.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfhreg/gpc_instrument.h	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfhreg/gpc_instrument.h	(revision 23594)
@@ -0,0 +1,81 @@
+/*                                             -*- c-file-style: "Ellemtel" -*-
+
+`gpc_instrument.h' - Pan-STARRS instrument FITS header keywords
+
+This file is part of version 0 of the FITS Keyword Registry.
+Read the `License' file for terms of use and distribution.
+Copyright 2004, Pan-STARRS, isani@ifa.hawaii.edu.
+
+___This header automatically generated from `Index'. Do not edit it here!___ */
+
+#include "macros.h"
+
+/*
+ * Description of columns:
+ *
+ *      TYPE - MK_BLN, MK_STR, MK_INT, MK_FLT, MK_PFL selects a type for the
+ *             keyword (True/False, 'String  ', integer, or floating point.)
+ *             Any keyword can still be set to any type with fh_setval_*(),
+ *             which takes a pre-formatted character string for the card.
+ *
+ *      IDX  - Index sorting number, which eventually determines the final
+ *             order of keywords in the FITS header.  These are floating point
+ *             values so new keywords can always be inserted in between
+ *             without shifting all the values.
+ *
+ *   KEYWORD - The name of the keyword.  Macros will be generated of the form
+ *             fh_get_KEYWORD(), fh_set_KEYWORD(), and fh_setval_KEYWORD().
+ *
+ *      PRC  - For MK_FLT(), this specifies the (scientific) precision,
+ *             or number of significant digits in the value.  If necessary,
+ *             scientific notation will be used to format the value (see
+ *             rules for printf "%.*G").
+ *             For MK_PFL(), this specifies mathematical precision, or number
+ *             of decimal places in the formatted value (see the rules for
+ *             printf "%.*f").
+ *
+ *             Use MK_PFL() (make "precise float") if that value is always
+ *             precise to an exact number digits after the decimal point.
+ *             Use MK_FLT() if the value might require scientific notation.
+ *             All results are FITS-standard legal.
+ *
+ *   COMMENT - String to be included after the / in the FITS card, if
+ *             there is room within the 80 columns (it may get truncated.)
+ *             Note that in the text below, 80 columns are reached when
+ *             there is still one space between the " and the ) at the end
+ *             of the line.
+ *
+ * FITS Structure : 0.000 to 9.999
+ *
+ * The following keywords give miscellaneous information about
+ * the instrument in general that doesn't really fit anywhere 
+ * else.
+ * 
+ * Instrument : 1000.000 to 1999.999
+ *
+ *TYPE --IDX--  KEYWORD- PRC -------------------COMMENT----------------------*/
+MK_CMT( 1000.00,CMTINST1,   ""                                                )
+MK_CMT( 1000.10,CMTINST2,   "Instrument Information"                          )
+MK_CMT( 1000.20,CMTINST3,   "----------------------"                          )
+MK_CMT( 1000.40,CMTINST4,   ""                                                )
+MK_PFL( 1009.0, PRESSURE,6, "[Torr] Micropirani pressure reading"             )
+MK_PFL( 1009.10,L3HUMID, 1, "[%] Relative humidity in space above L3"         )
+MK_PFL( 1009.11,L3AIRTMP,1, "[C] Air temperature in space above L3"           )
+MK_PFL( 1009.12,L3DEWPT, 1, "[C] Calculated dew point in space above L3"      )
+MK_STR( 1010.0, FILTSTAT,   "Filter mechanism status"                         )
+MK_STR( 1010.1, FILTERID,   "Filter glass identification"                     )
+MK_STR( 1010.2, FILTER  ,   "Filter band name OPEN,u,g,r,i,z,y,w"             )
+MK_STR( 1011.0, VIDMODE,    "Video mode used (w pix x h pix x rate Hz)"       )
+MK_PFL( 1110.1, DEWTEM1, 1, "[K] Dewar rtd 1 (CTI-Left head)"                 )
+MK_PFL( 1110.2, DEWTEM2, 1, "[K] Dewar rtd 2 (Copper plate)"                  )
+MK_PFL( 1110.3, DEWTEM3, 1, "[K] Dewar rtd 3 (OTA67 package)"                 )
+MK_PFL( 1110.4, DEWTEM4, 1, "[K] Dewar rtd 4 (Triangle post)"                 )
+MK_PFL( 1110.5, DEWTEM5, 1, "[K] Dewar rtd 5 (Copper bar)"                    )
+MK_PFL( 1110.6, DEWTEM6, 1, "[K] Dewar rtd 6 (CTI-Right head)"                )
+MK_PFL( 1210.1, SH_TEM,  1, "[K] Shack Hartmann head temperature"             )
+MK_INT( 1220.1, SH_POS    , "[steps] Shack Hartmann arm position"             )
+MK_INT( 1221.0, SH_STDLY  , "[2.04us/step] S-H arm step delay"                )
+MK_INT( 1222.0, SH_ACCEL  , "[2.04us/step^2] S-H acceleration param"          )
+MK_INT( 1223.0, SH_MNDLY  , "[2.04us/step] S-H minimum step delay"            )
+MK_INT( 1224.0, SH_ARMPN  , "motion controller input pins"                    )
+MK_PFL( 1225.0, SH_DIODE,1, "Vref/4096 (Vref=5V nominal) photodiode volts"    )
Index: /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfhreg/gpc_otguide.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfhreg/gpc_otguide.h	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfhreg/gpc_otguide.h	(revision 23594)
@@ -0,0 +1,82 @@
+/*                                             -*- c-file-style: "Ellemtel" -*-
+
+`gpc_otguide.h' - Pan-STARRS GPC OT correction and Guider keywords
+
+This file is part of version 0 of the FITS Keyword Registry.
+Read the `License' file for terms of use and distribution.
+Copyright 2004, Pan-STARRS, isani@ifa.hawaii.edu.
+
+___This header automatically generated from `Index'. Do not edit it here!___ */
+
+#include "macros.h"
+
+/*
+ * Description of columns:
+ *
+ *      TYPE - MK_BLN, MK_STR, MK_INT, MK_FLT, MK_PFL selects a type for the
+ *             keyword (True/False, 'String  ', integer, or floating point.)
+ *             Any keyword can still be set to any type with fh_setval_*(),
+ *             which takes a pre-formatted character string for the card.
+ *
+ *      IDX  - Index sorting number, which eventually determines the final
+ *             order of keywords in the FITS header.  These are floating point
+ *             values so new keywords can always be inserted in between
+ *             without shifting all the values.
+ *
+ *   KEYWORD - The name of the keyword.  Macros will be generated of the form
+ *             fh_get_KEYWORD(), fh_set_KEYWORD(), and fh_setval_KEYWORD().
+ *
+ *      PRC  - For MK_FLT(), this specifies the (scientific) precision,
+ *             or number of significant digits in the value.  If necessary,
+ *             scientific notation will be used to format the value (see
+ *             rules for printf "%.*G").
+ *             For MK_PFL(), this specifies mathematical precision, or number
+ *             of decimal places in the formatted value (see the rules for
+ *             printf "%.*f").
+ *
+ *             Use MK_PFL() (make "precise float") if that value is always
+ *             precise to an exact number digits after the decimal point.
+ *             Use MK_FLT() if the value might require scientific notation.
+ *             All results are FITS-standard legal.
+ *
+ *   COMMENT - String to be included after the / in the FITS card, if
+ *             there is room within the 80 columns (it may get truncated.)
+ *             Note that in the text below, 80 columns are reached when
+ *             there is still one space between the " and the ) at the end
+ *             of the line.
+ *
+ * FITS Structure : 0.000 to 9.999
+ *
+ * The following keywords give basic information about the structure and
+ * dimensions of the FITS file, and must appear in the specific order
+ * below, usually without any other intervening keywords.
+ *
+ * Guider : ???
+ *
+ *TYPE --IDX--  KEYWORD- PRC -------------------COMMENT----------------------*/
+MK_CMT(  250.00,CMTOTGD1 ,   ""                                               )
+MK_CMT(  250.01,CMTOTGD2 ,   "Guide Star Info"                                )
+MK_CMT(  250.02,CMTOTGD3 ,   "---------------"                                )
+MK_STR(  250.10,GS_ID,      "Guide star catalogue ID/name"                    )
+MK_PFL(  250.20,GS_PRRA,6,  "[degrees] Guide star predicted RA"               )
+MK_PFL(  250.30,GS_PRDEC,6, "[degrees] Guide star predicted DEC"              )
+MK_PFL(  250.40,GS_PRFP1,2, "[microns] Guide star pred. focal plane pos"      )
+MK_PFL(  250.50,GS_PRFP2,2, "[microns] Guide star pred. focal plane pos"      )
+MK_INT(  250.60,GS_PREDX,   "[unbinned pix] Guide star predicted X position"  )
+MK_INT(  250.70,GS_PREDY,   "[unbinned pix] Guide star predicted Y position"  )
+MK_PFL(  250.80,GS_PRMAG,2, "Guide star predicted magnitude with curr filter" )
+MK_STR(  251.07,GS_PSF,     "Video PSF algorithm used for centroid"           )
+MK_PFL( 251.081,GS_CORX, 6, "[pix] GS calc correction applied in camera X"    )
+MK_PFL( 251.082,GS_CORY, 6, "[pix] GS calc correction applied in camera Y"    )
+MK_PFL( 251.083,GS_CORPA,6, "[deg] GS calc correction applied to rotation"    )
+MK_PFL(  251.10,GS_CPIX1,2, "Guide star centroid from origin of CNAXIS system")
+MK_PFL(  251.20,GS_CPIX2,2, "Guide star centroid from origin of CNAXIS system")
+MK_PFL(  252.00,GS_FWHM, 2, "[pix] Guide star full width at half maximum"     )
+MK_PFL(  252.10,GS_FWHM1,2, "[pix] Guide star full width at half maximum in x")
+MK_PFL(  252.20,GS_FWHM2,2, "[pix] Guide star full width at half maximum in y")
+MK_PFL(  253.00,GS_PSKY, 2, "[adu] Sky level in guide box"                    )
+MK_PFL(  254.00,GS_PFLUX,2, "[adu] Total flux in star"                        )
+MK_PFL(  255.00,GS_SN   ,2, "Guide star signal to noise ratio"                )
+MK_PFL(  259.10,GS_AVFW ,2, "[pix] Average FWHM to present"                   )
+MK_PFL(  259.20,GS_AVX  ,2, "[microns] Average focal plane X pos to present"  )
+MK_PFL(  259.30,GS_AVY  ,2, "[microns] Average focal plane Y pos to present"  )
Index: /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfhreg/gpc_plant.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfhreg/gpc_plant.h	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfhreg/gpc_plant.h	(revision 23594)
@@ -0,0 +1,9 @@
+/*                                             -*- c-file-style: "Ellemtel" -*-
+
+`gpc_plant.h' - Pan-STARRS plant-related FITS header keywords
+
+This file is part of version 0 of the FITS Keyword Registry.
+Read the `License' file for terms of use and distribution.
+Copyright 2004, Pan-STARRS, isani@ifa.hawaii.edu.
+
+___This header automatically generated from `Index'. Do not edit it here!___ */
Index: /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfhreg/gpc_shutter.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfhreg/gpc_shutter.h	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfhreg/gpc_shutter.h	(revision 23594)
@@ -0,0 +1,71 @@
+/*                                             -*- c-file-style: "Ellemtel" -*-
+
+`gpc_instrument.h' - Pan-STARRS instrument FITS header keywords
+
+This file is part of version 0 of the FITS Keyword Registry.
+Read the `License' file for terms of use and distribution.
+Copyright 2004, Pan-STARRS, isani@ifa.hawaii.edu.
+
+___This header automatically generated from `Index'. Do not edit it here!___ */
+
+#include "macros.h"
+
+/*
+ * Description of columns:
+ *
+ *      TYPE - MK_BLN, MK_STR, MK_INT, MK_FLT, MK_PFL selects a type for the
+ *             keyword (True/False, 'String  ', integer, or floating point.)
+ *             Any keyword can still be set to any type with fh_setval_*(),
+ *             which takes a pre-formatted character string for the card.
+ *
+ *      IDX  - Index sorting number, which eventually determines the final
+ *             order of keywords in the FITS header.  These are floating point
+ *             values so new keywords can always be inserted in between
+ *             without shifting all the values.
+ *
+ *   KEYWORD - The name of the keyword.  Macros will be generated of the form
+ *             fh_get_KEYWORD(), fh_set_KEYWORD(), and fh_setval_KEYWORD().
+ *
+ *      PRC  - For MK_FLT(), this specifies the (scientific) precision,
+ *             or number of significant digits in the value.  If necessary,
+ *             scientific notation will be used to format the value (see
+ *             rules for printf "%.*G").
+ *             For MK_PFL(), this specifies mathematical precision, or number
+ *             of decimal places in the formatted value (see the rules for
+ *             printf "%.*f").
+ *
+ *             Use MK_PFL() (make "precise float") if that value is always
+ *             precise to an exact number digits after the decimal point.
+ *             Use MK_FLT() if the value might require scientific notation.
+ *             All results are FITS-standard legal.
+ *
+ *   COMMENT - String to be included after the / in the FITS card, if
+ *             there is room within the 80 columns (it may get truncated.)
+ *             Note that in the text below, 80 columns are reached when
+ *             there is still one space between the " and the ) at the end
+ *             of the line.
+ *
+ * Shutter: 200.000 to 209.999
+ *
+ * The following keywords give information about the shutter system
+ * for a given exposure.
+ *
+ *TYPE --IDX--  KEYWORD- PRC -------------------COMMENT----------------------*/
+MK_CMT(  200.0, CMTSHU1 ,   ""                                                )
+MK_CMT(  200.1, CMTSHU2 ,   "Shutter"                                         )
+MK_CMT(  200.2, CMTSHU3 ,   "-------"                                         )
+MK_STR(  201.0,	SHUTSTAT,   "Shutter status"                                  )
+MK_STR(  201.1,	SHUTERR,    "Shutter error code"                              )
+MK_STR(  202.00,SHUTOPEN,   "Shutter open time (TAI)"                         )
+MK_STR(  202.01,SHUTCLOS,   "Shutter close time (TAI)"                        )
+MK_PFL(  202.02,SHUTOPWN,6, "Shutter open uncertainty before SHUTOPEN (sec)"  )
+MK_PFL(  202.03,SHUTCLWN,6, "Shutter close uncertainty before SHUTCLOS (sec)" )
+MK_STR(  202.04,SHUTOUTC,   "Shutter open time (UTC)"                         )
+MK_STR(  202.05,SHUTCUTC,   "Shutter close time (UTC)"                        )
+MK_INT(  202.06,SHUTLEAP,   "Leap seconds used in TAI-UTC conversion (sec)"   )
+MK_PFL(  202.07,SHUTREQ, 6, "Requested exposure duration (sec)"               )
+MK_PFL(  202.08,SHUTTIME,6, "Actual exposure duration (sec)"                  )
+MK_STR(  202.09,SHUTOPBL,   "Shutter blade in aperture at start of exposure"  )
+MK_STR(  202.10,SHUTCLBL,   "Shutter blade in aperture at end of exposure"    )
+MK_STR(  209.0, SHUTDAVR,   "Shutter daemon version"                          )
+MK_STR(  209.1, SHUTDRVR,   "Shutter driver version"                          )
Index: /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfhreg/gpc_telescope.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfhreg/gpc_telescope.h	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfhreg/gpc_telescope.h	(revision 23594)
@@ -0,0 +1,166 @@
+/*                                             -*- c-file-style: "Ellemtel" -*-
+
+`gpc_telescope.h' - Pan-STARRS TCS FITS header keywords
+
+This file is part of version 0 of the FITS Keyword Registry.
+Read the `License' file for terms of use and distribution.
+Copyright 2004, Pan-STARRS, isani@ifa.hawaii.edu.
+
+___This header automatically generated from `Index'. Do not edit it here!___ */
+
+#include "macros.h"
+
+/*
+ * Description of columns:
+ *
+ *      TYPE - MK_BLN, MK_STR, MK_INT, MK_FLT, MK_PFL selects a type for the
+ *             keyword (True/False, 'String  ', integer, or floating point.)
+ *             Any keyword can still be set to any type with fh_setval_*(),
+ *             which takes a pre-formatted character string for the card.
+ *
+ *      IDX  - Index sorting number, which eventually determines the final
+ *             order of keywords in the FITS header.  These are floating point
+ *             values so new keywords can always be inserted in between
+ *             without shifting all the values.
+ *
+ *   KEYWORD - The name of the keyword.  Macros will be generated of the form
+ *             fh_get_KEYWORD(), fh_set_KEYWORD(), and fh_setval_KEYWORD().
+ *
+ *      PRC  - For MK_FLT(), this specifies the (scientific) precision,
+ *             or number of significant digits in the value.  If necessary,
+ *             scientific notation will be used to format the value (see
+ *             rules for printf "%.*G").
+ *             For MK_PFL(), this specifies mathematical precision, or number
+ *             of decimal places in the formatted value (see the rules for
+ *             printf "%.*f").
+ *
+ *             Use MK_PFL() (make "precise float") if that value is always
+ *             precise to an exact number digits after the decimal point.
+ *             Use MK_FLT() if the value might require scientific notation.
+ *             All results are FITS-standard legal.
+ *
+ *   COMMENT - String to be included after the / in the FITS card, if
+ *             there is room within the 80 columns (it may get truncated.)
+ *             Note that in the text below, 80 columns are reached when
+ *             there is still one space between the " and the ) at the end
+ *             of the line.
+ *
+ * FITS Structure : 0.000 to 9.999
+ *
+ * The following keywords give basic information about the structure and
+ * dimensions of the FITS file, and must appear in the specific order
+ * below, usually without any other intervening keywords.
+ *
+ *  * Telescope : 500.000 to 599.999
+ *
+ * Almost all keywords we define can be generated with the MK_XXX macros.
+ * The only exceptions are those keywords which contain a '-', since the
+ * '-' is not a legal character in a C identifier.  TCS uses four of them
+ * (DATE-OBS, UTC-OBS, MJD-OBS, and LST-OBS.)  The ID_XXX macros are used
+ * and the '-' is translated to a '_' (underscore) for use in C programs.
+ *
+ *TYPE --IDX--  KEYWORD- PRC -------------------COMMENT----------------------*/
+MK_CMT(  500.00,CMTPNT1 ,   ""                                                )
+MK_CMT(  500.10,CMTPNT2 ,   "Telescope and Pointing Information"              )
+MK_CMT(  500.20,CMTPNT3 ,   "----------------------------------"              )
+MK_CMT(  500.40,CMTPNT4 ,   ""                                                )
+MK_STR(  501.00,TELESCOP,   "Telescope"                                       )
+ID_PFL( 513.1,  MJD_OBS ,"MJD-OBS",7, "Modified Julian Date at start of obs." )
+ID_STR( 514.1,  LST_OBS ,"LST-OBS", "Local Sidereal time at start of obs."    )
+MK_STR(  503.0, TELSTAT ,   "Telescope control system status"                 )
+MK_STR(  503.1, DOMSTAT ,   "Telescope enclosure status"                      )
+MK_STR(  503.2, GUISTAT ,   "Telescope guiding system status"                 )
+MK_STR(  503.3, GUICONF ,   "Telescope guiding configuration"                 )
+MK_STR(  503.4, GUIKERN ,   "Filter kernel used for guiding"                  )
+MK_PFL(  503.5, GUIGAIX ,2, "Telescope guiding gain factor (x offset)"        )
+MK_PFL(  503.6, GUIGAIY ,2, "Telescope guiding gain factor (y offset)"        )
+MK_PFL(  503.7, GUIRATE ,1, "Telescope guiding rate (Hz)"                     )
+MK_VAL(  521.1, EQUINOX ,   "Telescope equinox of coordinates"                )
+MK_VAL(  521.2, EPOCH   ,   "Telescope equinox of coordinates"                )
+MK_PFL(  522.1, RA      ,6, "Telescope Right Ascension (degrees)"             )
+MK_PFL(  522.2, DEC     ,6, "Telescope Declination (degrees)"                 )
+MK_PFL(  522.3, AZ      ,6, "Telescope azimuth (degrees)"                     )
+MK_PFL(  522.4, ALT     ,6, "Telescope pointing altitude (degrees)"           )
+MK_PFL(  522.5, ROT     ,6, "Telescope rotator angle (degrees)"               )
+MK_PFL(  522.6, POSANGLE,6, "Telescope position angle (degrees)"              )
+MK_PFL(  522.7, PA_DEBUG,6, "Telescope position angle (alternate value)"      )
+MK_PFL(  523.0, COMRA   ,6, "Commanded telescope Right Ascension (degrees)"   )
+MK_PFL(  523.1, COMDEC  ,6, "Commanded telescope Declination (degrees)"       )
+MK_PFL(  523.2, COMAZ   ,6, "Commanded telescope azimuth (degrees)"           )
+MK_PFL(  523.3, COMALT  ,6, "Commanded telescope pointing altitude (degrees)" )
+MK_PFL(  523.4, COMROT  ,6, "Commanded telescope rotator angle (degrees)"     )
+MK_CMT(  530.00,CMTTELO1,   "NOTE: Telescope RA DEC or ALT AZ already include")
+MK_CMT(  530.10,CMTTELO2,   "      the following offsets.  Do not re-apply!"  )
+MK_PFL(  532.1, TELOFRA ,6, "Telescope offset in RA (degrees)"                )
+MK_PFL(  532.2, TELOFDEC,6, "Telescope offset in DEC (degrees)"               )
+MK_PFL(  532.3, TELOFAZ ,6, "Telescope offset in AZ (degrees)"                )
+MK_PFL(  532.4, TELOFALT,6, "Telescope offset in ALT (degrees)"               )
+MK_PFL(  532.5, TELOFROT,6, "Telescope offset in ROT (degrees)"               )
+MK_PFL(  532.7, TELOFX  ,6, "Telescope offset in camera X (degrees)"          )
+MK_PFL(  532.8, TELOFY  ,6, "Telescope offset in camera Y (degrees)"          )
+MK_PFL(  540.1, AIRMASS ,3, "Airmass at start of observation"                 )
+MK_PFL(  541.1, M1X     ,6, "Primary mirror x position (um)"                  )
+MK_PFL(  541.2, M1Y     ,6, "Primary mirror y position (um)"                  )
+MK_PFL(  541.3, M1Z     ,6, "Primary mirror z position (um)"                  )
+MK_PFL(  541.4, M1TIP   ,6, "Primary mirror tip (arcsec)"                     )
+MK_PFL(  541.5, M1TILT  ,6, "Primary mirror tilt (arcsec)"                    )
+MK_PFL(  542.1, M2X     ,6, "Secondary mirror x position (um)"                )
+MK_PFL(  542.2, M2Y     ,6, "Secondary mirror y position (um)"                )
+MK_PFL(  542.3, M2Z     ,6, "Secondary mirror z position (um)"                )
+MK_PFL(  542.4, M2TIP   ,6, "Secondary mirror tip (arcsec)"                   )
+MK_PFL(  542.5, M2TILT  ,6, "Secondary mirror tilt (arcsec)"                  )
+MK_STR(  543.0, M1M2MODV,   "Telescope mirror position model version"         )
+MK_PFL(  543.1, M1NOMX  ,6, "Modeled Primary mirror x position (um)"          )
+MK_PFL(  543.2, M1NOMY  ,6, "Modeled Primary mirror y position (um)"          )
+MK_PFL(  543.3, M1NOMZ  ,6, "Modeled Primary mirror z position (um)"          )
+MK_PFL(  543.4, M1NOMTIP,6, "Modeled Primary mirror tip (arcsec)"             )
+MK_PFL(  543.5, M1NOMTIL,6, "Modeled Primary mirror tilt (arcsec)"            )
+MK_PFL(  544.1, M2NOMX  ,6, "Modeled Secondary mirror x position (um)"        )
+MK_PFL(  544.2, M2NOMY  ,6, "Modeled Secondary mirror y position (um)"        )
+MK_PFL(  544.3, M2NOMZ  ,6, "Modeled Secondary mirror z position (um)"        )
+MK_PFL(  544.4, M2NOMTIP,6, "Modeled Secondary mirror tip (arcsec)"           )
+MK_PFL(  544.5, M2NOMTIL,6, "Modeled Secondary mirror tilt (arcsec)"          )
+MK_STR(  550.1, TELTEMTR,   "Mid truss temperatures (C)"                      )
+MK_STR(  550.2, TELTEMSP,   "Spider temperatures (C)"                         )
+MK_STR(  550.3, TELTEMMS,   "Primary mirror support temps (C)"                )
+MK_STR(  550.4, TELTEMM1,   "Primary mirror temps (C)"                        )
+MK_STR(  550.5, TELTEMM2,   "Secondary mirror temps (C)"                      )
+MK_STR(  550.6, TELTEMEX,   "Miscellaneous temperatures (C)"                  )
+
+/*
+ * Wavefront
+ */
+MK_PFL(  551.01,WVFA    ,14,"Wavefront A actuator force (N)"                  )
+MK_PFL(  551.02,WVFB    ,14,"Wavefront B actuator force (N)"                  )
+MK_PFL(  551.03,WVFC    ,14,"Wavefront C actuator force (N)"                  )
+MK_PFL(  551.04,WVFD    ,14,"Wavefront D actuator force (N)"                  )
+MK_PFL(  551.05,WVFE    ,14,"Wavefront E actuator force (N)"                  )
+MK_PFL(  551.06,WVFF    ,14,"Wavefront F actuator force (N)"                  )
+MK_PFL(  551.07,WVFG    ,14,"Wavefront G actuator force (N)"                  )
+MK_PFL(  551.08,WVFH    ,14,"Wavefront H actuator force (N)"                  )
+MK_PFL(  551.09,WVFI    ,14,"Wavefront I actuator force (N)"                  )
+MK_PFL(  551.10,WVFJ    ,14,"Wavefront J actuator force (N)"                  )
+MK_PFL(  551.11,WVFK    ,14,"Wavefront K actuator force (N)"                  )
+MK_PFL(  551.12,WVFL    ,14,"Wavefront L actuator force (N)"                  )
+
+/*
+ * These probably want to be in a different section... %%%
+ */
+MK_PFL(  560.1, ENVTEM,  1, "[C]           Weather, outside temperature"      )
+MK_PFL(  560.2, ENVHUM,  1, "[%]           Weather, relative humidity"        )
+MK_PFL(  560.3, ENVWIN,  1, "[m/s]         Weather, wind speed"               )
+MK_PFL(  560.4, ENVDIR,  1, "[deg E. of N] Weather, wind direction"           )
+MK_PFL(  561.0, SEEING,  1, "[arcsec]      Weather, seeing from DIMM, FWHM"   )
+
+/* 
+ * Other stuff Craig had put in general.h:
+ *
+ *TYPE --IDX--  KEYWORD- PRC -------------------COMMENT----------------------*/
+MK_PFL( 586.02, HA      ,16,"Hour angle at start (degrees)"                   )
+MK_PFL( 586.03,	ST      ,16,"Sidereal time at start (hours)"                  )
+MK_PFL( 586.04,	ZD      ,16,"Zenith distance (degrees)"                       )
+MK_STR( 587.00,	RASTRNG ,   "Actual Right Ascension (incl offset)"            )
+MK_STR( 587.01,	DECSTRNG,   "Actual Declination (incl offset)"                )
+MK_STR( 587.02,	HASTRNG ,   "Hour angle at start"                             )
+MK_STR( 587.03,	STSTRNG ,   "Sidereal time at start"                          )     
+MK_STR( 587.04,	ZDSTRNG ,   "Zenith distance (deg:mm:ss)"                     )                   
Index: /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfhreg/gpc_wcs.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfhreg/gpc_wcs.h	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfhreg/gpc_wcs.h	(revision 23594)
@@ -0,0 +1,103 @@
+/*                                             -*- c-file-style: "Ellemtel" -*-
+
+`gpc_instrument.h' - Pan-STARRS instrument FITS header keywords
+
+This file is part of version 0 of the FITS Keyword Registry.
+Read the `License' file for terms of use and distribution.
+Copyright 2004, Pan-STARRS, isani@ifa.hawaii.edu.
+
+___This header automatically generated from `Index'. Do not edit it here!___ */
+
+#include "macros.h"
+
+/*
+ * Description of columns:
+ *
+ *      TYPE - MK_BLN, MK_STR, MK_INT, MK_FLT, MK_PFL selects a type for the
+ *             keyword (True/False, 'String  ', integer, or floating point.)
+ *             Any keyword can still be set to any type with fh_setval_*(),
+ *             which takes a pre-formatted character string for the card.
+ *
+ *      IDX  - Index sorting number, which eventually determines the final
+ *             order of keywords in the FITS header.  These are floating point
+ *             values so new keywords can always be inserted in between
+ *             without shifting all the values.
+ *
+ *   KEYWORD - The name of the keyword.  Macros will be generated of the form
+ *             fh_get_KEYWORD(), fh_set_KEYWORD(), and fh_setval_KEYWORD().
+ *
+ *      PRC  - For MK_FLT(), this specifies the (scientific) precision,
+ *             or number of significant digits in the value.  If necessary,
+ *             scientific notation will be used to format the value (see
+ *             rules for printf "%.*G").
+ *             For MK_PFL(), this specifies mathematical precision, or number
+ *             of decimal places in the formatted value (see the rules for
+ *             printf "%.*f").
+ *
+ *             Use MK_PFL() (make "precise float") if that value is always
+ *             precise to an exact number digits after the decimal point.
+ *             Use MK_FLT() if the value might require scientific notation.
+ *             All results are FITS-standard legal.
+ *
+ *   COMMENT - String to be included after the / in the FITS card, if
+ *             there is room within the 80 columns (it may get truncated.)
+ *             Note that in the text below, 80 columns are reached when
+ *             there is still one space between the " and the ) at the end
+ *             of the line.
+ *
+ * WCS: 600.0 to 609.99
+ *
+ * The following keywords give information about the various WCS coordinate
+ * systems available for the FITS file.
+ *
+ *TYPE --IDX--  KEYWORD- PRC -------------------COMMENT----------------------*/
+MK_CMT( 600.0,  CMTWCS1 ,   ""                                                )
+MK_CMT( 600.1,  CMTWCS2 ,   "WCS Coordinate Systems"                          )
+MK_CMT( 600.2,  CMTWCS3 ,   "----------------------"                          )
+
+MK_STR( 601.10, CTYPE1  ,   "R.A. in tangent plane projection"                )
+MK_STR( 601.11, CUNIT1  ,   "Units for focal plane coord system axis 1"       )
+MK_PFL( 601.12, CRVAL1  ,6, "R.A. at ref. pixel"                              )
+MK_PFL( 601.13, CRPIX1  ,1, "Ref. pixel of first axis"                        )
+MK_PFL( 601.14, CDELT1  ,10,"R.A. pixel step"                                 )
+MK_STR( 601.15, CTYPE2  ,   "Dec. in tangent plane projection"                )
+MK_STR( 601.16, CUNIT2  ,   "Units for focal plane coord system axis 1"       )
+MK_PFL( 601.17, CRVAL2  ,6, "Dec. at ref. pixel"                              )
+MK_PFL( 601.18, CRPIX2  ,1, "Ref. pixel of second axis"                       )
+MK_PFL( 601.19, CDELT2  ,10,"Dec. pixel step"                                 )
+MK_PFL( 601.20, CD1_1   ,10,"CD transform matrix"                             )
+MK_PFL( 601.21, CD1_2   ,10,"CD transform matrix"                             )
+MK_PFL( 601.22, CD2_1   ,10,"CD transform matrix"                             )
+MK_PFL( 601.23, CD2_2   ,10,"CD transform matrix"                             )
+
+MK_STR( 602.00, WCSNAMEA,   "Redundant reiteration of default WCS system"     )
+MK_STR( 602.10, CTYPE1A ,   "R.A. in tangent plane projection"                )
+MK_STR( 602.11, CUNIT1A ,   "Units for focal plane coord system axis 1"       )
+MK_PFL( 602.12, CRVAL1A ,6, "R.A. at ref. pixel"                              )
+MK_PFL( 602.13, CRPIX1A ,1, "Ref. pixel of first axis"                        )
+MK_PFL( 602.14, CDELT1A ,10,"R.A. pixel step"                                 )
+MK_STR( 602.15, CTYPE2A ,   "Dec. in tangent plane projection"                )
+MK_STR( 602.16, CUNIT2A ,   "Units for focal plane coord system axis 1"       )
+MK_PFL( 602.17, CRVAL2A ,6, "Dec. at ref. pixel"                              )
+MK_PFL( 602.18, CRPIX2A ,1, "Ref. pixel of second axis"                       )
+MK_PFL( 602.19, CDELT2A ,10,"Dec. pixel step"                                 )
+MK_PFL( 602.20, CD1_1A  ,10,"CD transform matrix"                             )
+MK_PFL( 602.21, CD1_2A  ,10,"CD transform matrix"                             )
+MK_PFL( 602.22, CD2_1A  ,10,"CD transform matrix"                             )
+MK_PFL( 602.23, CD2_2A  ,10,"CD transform matrix"                             )
+
+MK_STR( 603.00, WCSNAMEU,   "Position on instrument focal plane"              )
+MK_STR( 603.10, CTYPE1U ,   "Linear instrument focal plane position"          )
+MK_STR( 603.11, CUNIT1U ,   "Units for focal plane coord system axis 1"       )
+MK_PFL( 603.12, CRVAL1U ,6, "Focal plane position at reference pixel"         )
+MK_PFL( 603.13, CRPIX1U ,1, "Ref. pixel of first axis"                        )
+MK_PFL( 603.14, CDELT1U ,10,"Pixel step"                                      )
+MK_STR( 603.15, CTYPE2U ,   "Linear instrument focal plane position"          )
+MK_STR( 603.16, CUNIT2U ,   "Units for focal plane coord system axis 2"       )
+MK_PFL( 603.17, CRVAL2U ,6, "Focal plane position at reference pixel"         )
+MK_PFL( 603.18, CRPIX2U ,1, "Ref. pixel of second axis"                       )
+MK_PFL( 603.19, CDELT2U ,10,"Pixel step"                                      )
+MK_PFL( 603.20, CD1_1U  ,10,"CD transform matrix"                             )
+MK_PFL( 603.21, CD1_2U  ,10,"CD transform matrix"                             )
+MK_PFL( 603.22, CD2_1U  ,10,"CD transform matrix"                             )
+MK_PFL( 603.23, CD2_2U  ,10,"CD transform matrix"                             )
Index: /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfhreg/junk
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfhreg/junk	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfhreg/junk	(revision 23594)
@@ -0,0 +1,686 @@
+
+/* ****************************************************
+ * *** World Coordinate System : 300.000 to 399.999 ***
+ * ****************************************************
+ *
+ *TYPE --IDX--  KEYWORD- PRC -------------------COMMENT----------------------*/
+
+/*
+ * Telescope : 500.000 to 599.999
+ *
+ * Almost all keywords we define can be generated with the MK_XXX macros.
+ * The only exceptions are those keywords which contain a '-', since the
+ * '-' is not a legal character in a C identifier.  TCS uses four of them
+ * (DATE-OBS, UTC-OBS, MJD-OBS, and LST-OBS.)  The ID_XXX macros are used
+ * and the '-' is translated to a '_' (underscore) for use in C programs.
+ *
+ * NOTE: TELESCOP, TELSTAT, TELCONF, LATITUDE, and LONGITUD are currently
+ * inserted by DetCom and _not_ tcsh.  This should probably be changed.
+ *
+ * Further details on some less obvious keywords:
+ *
+ *   SKYFLUX  - if the guider(s) can generate a reasonable guess for the
+ *              amount of flux currently available for doing flats, that
+ *              number is in this keyword - negative means not available
+ *
+ *   GUIFLUXn - these values are the most recent one minute average of the
+ *              total flux seen in whatever guiding box/area is in use
+ *
+ *   GUIFWH?n - these are the most recent one minute average FWHM values
+ *              for the x/y axes of the guider image(s) recorded at the
+ *              start of the exposure
+ *
+ *   ISUSTDV? - these are the most recent one minute standard deviations
+ *              in the ISU x and y positions in arc seconds recorded
+ *              at the start of the exposure
+ *
+ *   FSAstats - the FSA statistics are for the most recent 10 minutes
+ *              recorded at the start of the exposure
+ *
+ * If there are multiple guiders in use, the unnumbered GUIEQUIN/GUIRADEC
+ * /GUIRA/GUIDEC/GUIRAPM/GUIDECPM values will duplicate the numbered
+ * values for the primary, reference guider.  If there is only one guider
+ * in use, only the unnumbered values should be present.
+ *
+ *TYPE --IDX--  SYMBOL--  KEYWORD-  -----------COMMENT----------------------*/
+ID_STR( 511.1,	DATE_OBS,"DATE-OBS", "Date at start of observation (UTC)"     )
+ID_STR( 512.1,	UTC_OBS ,"UTC-OBS", "Time at start of observation (UTC)"      )
+ID_PFL( 513.1,  MJD_OBS ,"MJD-OBS",7, "Modified Julian Date at start of obs." )
+ID_STR( 514.1,	LST_OBS ,"LST-OBS", "Sidereal time at start of exposure"      )
+
+/*TYPE --IDX--  KEYWORD- PRC -------------------COMMENT----------------------*/
+MK_CMT(  500.1, CMTTCS1 ,   ""                                                )
+MK_CMT(  500.2, CMTTCS2 ,   "Telescope"                                       )
+MK_CMT(  500.3, CMTTCS3 ,   "---------"                                       )
+MK_CMT(  500.4, CMTTCS4 ,   ""                                                )
+MK_STR(  501.1, TELESCOP,   ""                                                )
+MK_STR(  501.2, ORIGIN  ,   "Canada-France-Hawaii Telescope"                  )
+MK_PFL(  502.1, LATITUDE,6, "Latitude (degrees N)"                            )
+MK_PFL(  502.2, LONGITUD,6, "Longitude (degrees E)"                           )
+MK_STR(  503.0, TELSTAT ,   "Telescope Control System status"                 )
+MK_STR(  510.0, TIMESYS ,   "Time System for DATExxxx"                        )
+/* note that the following three have xxx.1 versions above */
+MK_STR(  512.0,	UTIME   ,   "Time at start of observation (UT)"               )
+MK_PFL(  513.0,	MJDATE  ,7 ,"Modified Julian Date at start of observation"    )
+MK_STR(  514.0,	SIDTIME ,   "Sidereal time at start of observation"           )
+MK_VAL(  521.0,	EPOCH   ,   "Equinox of coordinates"                          )
+MK_VAL(  521.1,	EQUINOX ,   "Equinox of coordinates"                          )
+MK_STR(  522.0,	RADECSYS,   "Coordinate system for equinox (FK4/FK5/GAPPT)"   )
+MK_STR(  522.1,	RA      ,   "Object right ascension"                          )
+MK_STR(  522.2,	DEC     ,   "Object declination"                              )
+MK_PFL(  523.1,	RA_DEG  ,6, "Object right ascension in degrees"               )
+MK_PFL(  523.2,	DEC_DEG ,6, "Object declination in degrees"                   )
+
+MK_PFL(525.101,	CRVAL1	,5, "WCS Ref value (RA in decimal degrees)"           )
+MK_PFL(525.102,	CRVAL2	,5, "WCS Ref value (DEC in decimal degrees)"          )
+MK_STR(525.211,	CTYPE1	,   "WCS Coordinate type"                             )
+MK_STR(525.212,	CTYPE2	,   "WCS Coordinate type"                             )
+MK_PFL(525.301,	CRPIX1	,1, "WCS Coordinate reference pixel"                  )
+MK_PFL(525.302,	CRPIX2	,1, "WCS Coordinate reference pixel"                  )
+MK_FLT(525.411,	CD1_1	,6, "WCS Coordinate scale matrix"                     )
+MK_FLT(525.412,	CD1_2	,6, "WCS Coordinate scale matrix"                     )
+MK_FLT(525.421,	CD2_1	,6, "WCS Coordinate scale matrix"                     )
+MK_FLT(525.422,	CD2_2	,6, "WCS Coordinate scale matrix"                     )
+
+MK_INT(  530.0,	NGUIDER	,   "TCS Number of guiders"                           )
+MK_INT( 530.01, NGUISTAR,   "TCS number of guide stars/probes in use"         )
+MK_STR(  530.1, GUINAME ,   "TCS guider name"                                 )
+MK_VAL( 530.21,	GUIEQUIN,   "TCS guider equinox"                              )
+MK_STR( 530.22,	GUIRADEC,   "TCS guider system for equinox"                   )
+MK_STR( 530.23,	GUIRA   ,   "TCS guider right ascension"                      )
+MK_STR( 530.24,	GUIDEC  ,   "TCS guider declination"                          )
+MK_PFL( 530.25, GUIRAPM ,2, "TCS guider right ascension proper motion arcsec" )
+MK_PFL( 530.26, GUIDECPM,2, "TCS guider declination proper motion arcsec"     )
+MK_STR(  530.3, GUIOBJN ,   "TCS guider object name"                          )
+MK_PFL(  530.4, GUIMAGN ,1, "TCS guider object magnitude"                     )
+MK_PFL( 530.51,	XPROBE  ,3 ,"Telescope bonnette guide probe X position"       )
+MK_PFL( 530.52,	YPROBE  ,3 ,"Telescope bonnette guide probe Y position"       )
+MK_PFL( 530.53,	ZPROBE  ,3 ,"Telescope bonnette guide probe Z position"       )
+MK_INT(  530.6, GUIFLUX ,   "TCS guider flux"                                 )
+MK_PFL( 530.71, GUIFWHX ,2, "TCS guider average x FWHM in pixels"             )
+MK_PFL( 530.72, GUIFWHY ,2, "TCS guider average y FWHM in pixels"             )
+MK_INT(  530.8, SKYFLUX ,   "TCS total sky flux"                              )
+
+MK_STR(  531.1,	GUINAME1,   "TCS guider #1 identification"                    )
+MK_VAL( 531.21,	GUIEQUI1,   "TCS guider #1 equinox"                           )
+MK_STR( 531.22,	GUIRADE1,   "TCS guider #1 system for equinox"                )
+MK_STR( 531.23,	GUIRA1  ,   "TCS guider #1 right ascension"                   )
+MK_STR( 531.24,	GUIDEC1 ,   "TCS guider #1 declination"                       )
+MK_PFL( 531.25, GUIRAPM1,2, "TCS guider #1 right ascen proper motion arcsec"  )
+MK_PFL( 531.26, GUIDEPM1,2, "TCS guider #1 declination proper motion arcsec"  )
+MK_STR(  531.3, GUIOBJN1,   "TCS guider #1 object name"                       )
+MK_PFL(	 531.4,	GUIMAGN1,1, "TCS guider #1 object magnitude"                  )
+MK_PFL( 531.51, GUIPOSX1,3, "TCS guider #1 probe x position"                  )
+MK_PFL( 531.52, GUIPOSY1,3, "TCS guider #1 probe y position"                  )
+MK_PFL( 531.53, GUIPOSZ1,3, "TCS guider #1 probe z position"                  )
+MK_INT(  531.6, GUIFLUX1,   "TCS guider #1 flux"                              )
+MK_PFL( 531.71, GUIFWHX1,2, "TCS guider #1 average x FWHM in pixels"          )
+MK_PFL( 531.72, GUIFWHY1,2, "TCS guider #1 average y FWHM in pixels"          )
+
+MK_STR(  532.1,	GUINAME2,   "TCS guider #2 identification"                    )
+MK_VAL( 532.21,	GUIEQUI2,   "TCS guider #2 equinox"                           )
+MK_STR( 532.22,	GUIRADE2,   "TCS guider #2 system for equinox"                )
+MK_STR( 532.23,	GUIRA2  ,   "TCS guider #2 right ascension"                   )
+MK_STR( 532.24,	GUIDEC2 ,   "TCS guider #2 declination"                       )
+MK_PFL( 532.25, GUIRAPM2,2, "TCS guider #2 right ascen proper motion arcsec"  )
+MK_PFL( 532.26, GUIDEPM2,2, "TCS guider #2 declination proper motion arcsec"  )
+MK_STR(  532.3, GUIOBJN2,   "TCS guider #2 object name"                       )
+MK_PFL(	 532.4,	GUIMAGN2,1, "TCS guider #2 object magnitude"                  )
+MK_PFL( 532.51, GUIPOSX2,3, "TCS guider #2 probe x position"                  )
+MK_PFL( 532.52, GUIPOSY2,3, "TCS guider #2 probe y position"                  )
+MK_PFL( 532.53, GUIPOSZ2,3, "TCS guider #2 probe z position"                  )
+MK_INT(  532.6, GUIFLUX2,   "TCS guider #2 flux"                              )
+MK_PFL( 532.71, GUIFWHX2,2, "TCS guider #2 average x FWHM in pixels"          )
+MK_PFL( 532.72, GUIFWHY2,2, "TCS guider #2 average y FWHM in pixels"          )
+
+MK_STR(  533.1,	GUINAME3,   "TCS guider #3 identification"                    )
+MK_VAL( 533.21,	GUIEQUI3,   "TCS guider #3 equinox"                           )
+MK_STR( 533.22,	GUIRADE3,   "TCS guider #3 system for equinox"                )
+MK_STR( 533.23,	GUIRA3  ,   "TCS guider #3 right ascension"                   )
+MK_STR( 533.24,	GUIDEC3 ,   "TCS guider #3 declination"                       )
+MK_PFL( 533.25, GUIRAPM3,2, "TCS guider #3 right ascen proper motion arcsec"  )
+MK_PFL( 533.26, GUIDEPM3,2, "TCS guider #3 declination proper motion arcsec"  )
+MK_STR(  533.3, GUIOBJN3,   "TCS guider #3 object name"                       )
+MK_PFL(	 533.4,	GUIMAGN3,1, "TCS guider #3 object magnitude"                  )
+MK_PFL( 533.51, GUIPOSX3,3, "TCS guider #3 probe x position"                  )
+MK_PFL( 533.52, GUIPOSY3,3, "TCS guider #3 probe y position"                  )
+MK_PFL( 533.53, GUIPOSZ3,3, "TCS guider #3 probe z position"                  )
+MK_INT(  533.6, GUIFLUX3,   "TCS guider #3 flux"                              )
+MK_PFL( 533.71, GUIFWHX3,2, "TCS guider #3 average x FWHM in pixels"          )
+MK_PFL( 533.72, GUIFWHY3,2, "TCS guider #3 average y FWHM in pixels"          )
+
+MK_STR(  534.1,	GUINAME4,   "TCS guider #4 identification"                    )
+MK_VAL( 534.21,	GUIEQUI4,   "TCS guider #4 equinox"                           )
+MK_STR( 534.22,	GUIRADE4,   "TCS guider #4 system for equinox"                )
+MK_STR( 534.23,	GUIRA4  ,   "TCS guider #4 right ascension"                   )
+MK_STR( 534.24,	GUIDEC4 ,   "TCS guider #4 declination"                       )
+MK_PFL( 534.25, GUIRAPM4,2, "TCS guider #4 right ascen proper motion arcsec"  )
+MK_PFL( 534.26, GUIDEPM4,2, "TCS guider #4 declination proper motion arcsec"  )
+MK_STR(  534.3, GUIOBJN4,   "TCS guider #4 object name"                       )
+MK_PFL(	 534.4,	GUIMAGN4,1, "TCS guider #4 object magnitude"                  )
+MK_PFL( 534.51, GUIPOSX4,3, "TCS guider #4 probe x position"                  )
+MK_PFL( 534.52, GUIPOSY4,3, "TCS guider #4 probe y position"                  )
+MK_PFL( 534.53, GUIPOSZ4,3, "TCS guider #4 probe z position"                  )
+MK_INT(  534.6, GUIFLUX4,   "TCS guider #4 flux"                              )
+MK_PFL( 534.71, GUIFWHX4,2, "TCS guider #4 average x FWHM in pixels"          )
+MK_PFL( 534.72, GUIFWHY4,2, "TCS guider #4 average y FWHM in pixels"          )
+
+MK_PFL(  540.1,	AIRMASS ,3, "Airmass at start of observation"                 )
+MK_PFL(  540.2, TELALT  ,2, "Telescope altitude at start of observation, deg" )
+MK_PFL(  540.3, TELAZ   ,2, "Telescope azimuth at start, deg, 0=N 90=E 270=W" )
+MK_PFL(  540.4, MOONANGL,2, "Angle from object to moon at start in degrees"   )
+MK_STR(  550.0,	FOCUSID ,   "Telescope focus in use"                          )
+MK_STR(  550.1, TELCONF ,   "Telescope focus in use"                          )
+MK_INT(  551.0,	FOCUSPOS,   "Telescope focus encoder readout"                 )
+MK_INT(  551.1,	TELFOCUS,   "Telescope focus encoder readout"                 )
+MK_PFL(  552.0,	BONANGLE,2, "Telescope bonnette rotation angle in degrees"    )
+MK_PFL(  552.1,	ROTANGLE,2, "Telescope bonnette rotation angle in degrees"    )
+
+/*
+ * The following values perhaps should have their own section, but they
+ * are also (with some stretch) a part of TCS so for now they are here.
+ * They should only be included in MegaPrime and WIRCam images.
+ */
+
+MK_PFL(  560.1, ISUGAIN ,2, "Instrument Stabilization Unit control gain"      )
+MK_PFL(  560.2, ISURATE ,2, "ISU control rate in Hz"                          )
+MK_PFL(  560.3, ISUTCSOR,2, "ISU offload rate in Hz to telescope position"    )
+MK_STR(  560.4, ISUSTATE,   "ISU control state (Off/Only/TCS/Full/Frozen)"    )
+MK_PFL(  560.5, ISUSTDVX,2, "ISU most recent minute std dev in x in arcsec"   )
+MK_PFL(  560.6, ISUSTDVY,2, "ISU most recent minute std dev in y in arcsec"   )
+
+/*
+ * The following values perhaps should have their own section, but they
+ * are also (with some stretch) a part of TCS so for now they are here.
+ * They should only be included in MegaPrime images.
+ */
+
+MK_PFL(  570.1, FSAGAIN ,2, "Focus Stage Assembly movement control gain"      )
+MK_PFL(  570.2, FSARATE ,2, "FSA control rate in Hz"                          )
+MK_PFL(  570.3, FSATHRES,3, "FSA movement threshold in mm"                    )
+MK_STR(  570.4, FSASTATE,   "FSA control state (Off/On/Paused)"               )
+MK_INT(  570.5, FSANMOVE,   "FSA number of actual moves in last 10 minutes"   )
+MK_PFL( 570.61, FSAMINZ ,3, "FSA minimum position in last 10 minutes"         )
+MK_PFL( 570.62, FSAMAXZ ,3, "FSA maximum position in last 10 minutes"         )
+MK_PFL( 570.63, FSAMEANZ,3, "FSA mean position in last 10 minutes"            )
+MK_PFL( 570.64, FSASTDVZ,3, "FSA position std dev in last 10 minutes"         )
+
+/*
+ * The following values are used to collect data for TCS pointing model
+ * tuning.  Some of the values duplicate numbers elsewhere in the headers,
+ * but where it matters these are all read at the same time in the IOC.
+ */
+
+MK_STR( 580.11, TCSGPSBC,   "TCS GPS read out in BCD"                         )
+MK_PFL( 580.12, TCSGPSTM,5, "TCS GPS clock time in decimal hours"             )
+MK_STR( 580.13, TCSRBUSS,   "TCS RBUSS clock time"                            )
+MK_STR( 580.14, TCSEPICS,   "TCS EPICS clock time"                            )
+MK_STR( 580.15, TCSAMODE,   "TCS acquisition mode - T/O/G/g for t/o/g coords" )
+MK_PFL( 580.21, TCSMJD  ,7, "TCS MJD"                                         )
+MK_PFL( 580.22, TCSLST  ,5, "TCS LST in decimal hours"                        )
+MK_PFL( 580.31, TCSAPHA ,4, "TCS apparent hour angle in degrees"              )
+MK_PFL( 580.32, TCSAPDEC,4, "TCS apparent declination in degrees"             )
+MK_PFL( 580.41, TCSOBHA ,4, "TCS observed hour angle in degrees"              )
+MK_PFL( 580.42, TCSOBDEC,4, "TCS observed declination in degrees"             )
+MK_INT( 580.51, TCSENHA ,   "TCS hour angle encoder bits reading"             )
+MK_INT( 580.52, TCSENDEC,   "TCS declination encoder bits reading"            )
+MK_PFL( 580.61, TCSEDHA ,4, "TCS hour angle encoder in degrees"               )
+MK_PFL( 580.62, TCSEDDEC,4, "TCS declination encoder in degrees"              )
+MK_PFL( 580.71, TCSMVRA ,4, "TCS right ascension acquisition move in arcsec"  )
+MK_PFL( 580.72, TCSMVDEC,4, "TCS declination acquisition move in arcsec"      )
+MK_PFL( 580.81, TCSMVX  ,3, "TCS secondary guider acquisition x move in mm"   )
+MK_PFL( 580.82, TCSMVY  ,3, "TCS secondary guider acquisition y move in mm"   )
+
+/*
+ * Adaptive Optics : 700.000 to 799.999
+ *
+ *
+ *TYPE --IDX--  KEYWORD- PRC -------------------COMMENT----------------------*/
+
+MK_CMT(  700.1, CMTAOB1 ,   ""                                                )
+MK_CMT(  700.2, CMTAOB2 ,   "Adaptive Optics Bonnette"                        )
+MK_CMT(  700.3, CMTAOB3 ,   "------------------------"                        )
+MK_CMT(  700.4, CMTAOB4 ,   ""                                                )
+
+/* 710 through 719 are input control values to the real time system */
+
+/*
+ * state of AO correction (terms come from original description of loop
+ * operation which was deemed not PC and changed to "correction")
+ */
+MK_STR(  711.0, AOBLOOP ,   "AOB closed loop Open/Closed"                     )
+/*
+ * primary gain in the integrator providing signals to the bimorph
+ */
+MK_PFL(  712.0, LOOPGAIN,1, "Gain of the integrator"                          )
+/*
+ * correction can be fully automatic (all modes, bimorph offloading to
+ * tip/tilt mirror - nested), manual (manually selected modes, tip/tilt
+ * mode if selected only feeds tip/tilt mirror, i.e., not via bimorph
+ * - single), or tip/tilt only (bimorph not used at all, kept flat)
+ */
+MK_STR(  713.0, LOOPNES ,   "Loop type \"Nested\"/\"Tip/Tilt\"/\"Single\""    )
+/*
+ * in automatic correction this is the gain between the bimorph and the
+ * tip/tilt mirror
+ */
+MK_PFL(  714.0, LOOPNESG,1, "Nested loop gain"                                )
+/*
+ * the individual mode gains can be fixed or optimized based on signal
+ * levels
+ */
+MK_STR(  715.0, LOOPOPT ,   "Optimized loop control True/False"               )
+/*
+ * stroke of the membrane mirror (1-256)
+ */
+MK_INT(  716.0, WFSGOPT ,   "WFS optical gain"                                )
+/*
+ * integration time for each calculation cycle
+ */
+MK_PFL(  717.0, WFSSAMP ,3, "WFS sampling period (sec)"                       )
+
+/* 720-729 are measured values calculated by the real time system */
+
+/*
+ * calculated value of R0
+ */
+MK_PFL(  721.0, R0      ,1, "Fried parameter (cm)"                            )
+/*
+ * sum of the photon counts on the 19 APD's, for the most recent integration
+ */
+MK_INT(  722.0, WFSCOUNT,   "Total flux count on WFS"                         )
+
+/* 750-769 are input control values to the bench control ProLog(tm) */
+
+/*
+ * whether the atmospheric dispersion corrector (ADC) is in or out of the
+ * optical path
+ */
+MK_STR(  751.0, ADCPOS  ,   "AOB ADC position In/Out"                         )
+/*
+ * angle of the dispersion caused by the ADC
+ */
+MK_PFL(  752.0, ADCANGLE,2, "AOB ADC angle (degrees)"                         )
+/*
+ * amount of dispersion caused by the relative angle between the ADC prisms
+ */
+MK_PFL(  753.0, ADCPOWER,4, "AOB ADC power"                                   )
+/*
+ * beam splitter id code read from slugs on the splitters
+ */
+MK_INT(  754.0, BEAMSPID,   "Beam splitter ID"                                )
+/*
+ * beam splitter description based on the above codes
+ */
+MK_STR(  755.0, BEAMSP  ,   "Beam splitter description"                       )
+/*
+ * whether the AOB central mirror is in (AO system in light path)
+ * or out (AO system not being used)
+ */
+MK_STR(  756.0, MIRSLIDE,   "AOB mirror slide \"F/20\"/\"F/8\""               )
+/*
+ * neutral density filter position (in optical path to WFS)
+ *   0 == home
+ *   1 == no filter (close but not quite the same physical position as home)
+ *   2 == 1.0 density filter
+ *   3 == 2.0    "      "
+ *   4 == 3.0    "      "
+ */
+MK_INT(  757.0, WFSNDID ,   "WFS ND filter position"                          )
+/*
+ * following 3 are the Wave Front Sensor (WFS) position in a coordinate
+ * system close to having z parallel to the optical path
+ */
+MK_PFL(  761.0, WFSX    ,1, "Translated WFS X coord (steps)"                  )
+MK_PFL(  762.0, WFSY    ,1, "Translated WFS Y coord (steps)"                  )
+MK_PFL(  763.0, WFSZ    ,1, "Translated WFS Z coord (steps)"                  )
+/*
+ * Calibration Sources : 900.000 to 999.999
+ *
+ *
+ *TYPE --IDX--  KEYWORD- PRC -------------------COMMENT----------------------*/
+
+MK_CMT(  900.1, CMTCAL1 ,   ""                                                )
+MK_CMT(  900.2, CMTCAL2 ,   "Calibration sources"                             )
+MK_CMT(  900.3, CMTCAL3 ,   "-------------------"                             )
+MK_CMT(  900.4, CMTCAL4 ,   ""                                                )
+
+/* 910 - 919 are flat field lamps */
+
+/*
+ * Gecko lamps are computer controlled, these give On/Off status and
+ * intensity
+ */
+MK_STR(  910.0, FFLAMPON,   "flatfield lamp status ON/OFF"                    )
+MK_INT(  911.0, FFLAMP  ,   "flatfield lamp intensity"                        )
+/* add which lamp if get control of dome flat fields */
+
+/* 920-999 are calibration lamp sources */
+
+/*
+ * Gecko has 4 lamps, these give On/Off status and descriptions
+ */
+MK_STR(  920.0, CLAMP0ON,   "comparison lamp 0 status ON/OFF"                 )
+MK_STR(  920.1, CLAMP0  ,   "comparison lamp 0 description"                   )
+MK_STR(  921.0, CLAMP1ON,   "comparison lamp 1 status ON/OFF"                 )
+MK_STR(  921.1, CLAMP1  ,   "comparison lamp 1 description"                   )
+MK_STR(  922.0, CLAMP2ON,   "comparison lamp 2 status ON/OFF"                 )
+MK_STR(  922.1, CLAMP2  ,   "comparison lamp 2 description"                   )
+MK_STR(  923.0, CLAMP3ON,   "comparison lamp 3 status ON/OFF"                 )
+MK_STR(  923.1, CLAMP3  ,   "comparison lamp 3 description"                   )
+
+/*
+ * GumBall has 10 lamps, though the last two are Fabre-Perot lamps sharing
+ * the same light path and cannot be on together, these give On/Off status
+ * for each
+ */
+MK_STR(  930.0, CALIBL0 ,   "lamp 0 ON/OFF"                                   )
+MK_STR(  931.0, CALIBL1 ,   "lamp 1 ON/OFF"                                   )
+MK_STR(  932.0, CALIBL2 ,   "lamp 2 ON/OFF"                                   )
+MK_STR(  933.0, CALIBL3 ,   "lamp 3 ON/OFF"                                   )
+MK_STR(  934.0, CALIBL4 ,   "lamp 4 ON/OFF"                                   )
+MK_STR(  935.0, CALIBL5 ,   "lamp 5 ON/OFF"                                   )
+MK_STR(  936.0, CALIBL6 ,   "lamp 6 ON/OFF"                                   )
+MK_STR(  937.0, CALIBL7 ,   "lamp 7 ON/OFF"                                   )
+MK_STR(  938.0, CALIBL8 ,   "lamp 8 ON/OFF"                                   )
+MK_STR(  939.0, CALIBL9 ,   "lamp 9 ON/OFF"                                   )
+
+/*
+ * Instrument : 1000.000 to 1999.999
+ *
+ *TYPE --IDX--  KEYWORD- PRC -------------------COMMENT----------------------*/
+
+MK_CMT( 1000.1, CMTINST1,   ""                                                )
+MK_CMT( 1000.2, CMTINST2,   "Instrument Description"                          )
+MK_CMT( 1000.3, CMTINST3,   "----------------------"                          )
+MK_CMT( 1000.4, CMTINST4,   ""                                                )
+
+/*
+ * There are lots of shared keywords among the instruments and spectrographs.
+ * Where the keywords are really general purpose (e.g., FILTER) they are
+ * separated from the instrument areas.  Where the keywords are more specific,
+ * they are associated with the "first" (arbitrarily defined as starting about
+ * 1992 :-) instrument that used them.
+ */
+
+/* 1010 through 1099 are filter wheel info */
+
+/*
+ * Somewhere in the optical train there be filters.  If there is one filter
+ * use the FILTER sequence, whether it is actually in the detector or the
+ * instrument.  If there are two filters (usual case for IR), use the WHEEL
+ * sequence.  For an IR camera on an instrument with a filter, use both.
+ */
+
+/*
+ * code descriptions for all wheel identifiers
+ *
+ *  ID - numeric position number of wheel
+ *  <null> or DE - text description of filter mounted in that position
+ *  LB - lower bound of filter transmission - units Angstrom or microns?
+ *  UB - upper bound of filter transmission - units Angstrom or microns?
+ *  BW - bandwidth of filter transmission - units Angstrom or microns?
+ *  WL - center wave length of filter transmission - units Angstrom or microns?
+ */
+
+/* single filter wheel */
+MK_INT( 1010.1, FILTERID,   "wheel position"                                  )
+MK_STR( 1010.2, FILTER  ,   "description of filter"                           )
+MK_FLT( 1010.3, FILTERBW,3, "filter bandwidth"                                )
+MK_FLT( 1010.4, FILTERWL,3, "filter wavelength"                               )
+MK_FLT( 1010.5, FILTERLB,5, "lower bound of filter (units/%?)"                )
+MK_FLT( 1010.6, FILTERUB,5, "upper bound of filter (units/%?)"                )
+/* MOS/SIS have coded filter wheels, this is the code for the installed one */
+MK_INT( 1010.7, FILTSLID,   "filter drawer number"                            )
+
+/* first filter wheel of two */
+MK_INT( 1020.1, WHEELAID,   "'wheel' A position"                              )
+MK_STR( 1020.2, WHEELADE,   "description of filter"                           )
+MK_FLT( 1020.5, WHEELALB,4, "lower bound of filter A (units/%?)"              )
+MK_FLT( 1020.6, WHEELAUB,4, "upper bound of filter A (units/%?)"              )
+/* second filter wheel of two */
+MK_INT( 1030.1, WHEELBID,   "'wheel' B position"                              )
+MK_STR( 1030.2, WHEELBDE,   "description of filter"                           )
+MK_FLT( 1030.5, WHEELBLB,4, "lower bound of filter B (units/%?)"              )
+MK_FLT( 1030.6, WHEELBUB,4, "upper bound of filter B (units/%?)"              )
+
+/* 1100 through 1199 are general instrument set up values */
+
+/*
+ * For instruments that have an internal focus adjustment, this records
+ * the current position
+ */
+MK_FLT( 1101.0, INSTFOC ,7, "internal instrument focus value"                 )
+
+
+/*
+ * Spectrograph : 4000.000 to 4999.999
+ *
+ *TYPE --IDX--  KEYWORD- PRC -------------------COMMENT----------------------*/
+
+MK_CMT( 4000.1, CMTSPEC1,   ""                                                )
+MK_CMT( 4000.2, CMTSPEC2,   "Spectrograph Description"                        )
+MK_CMT( 4000.3, CMTSPEC3,   "------------------------"                        )
+MK_CMT( 4000.4, CMTSPEC4,   ""                                                )
+
+/* 4100 through 4199 are MOS/SIS */
+
+/*
+ * There are a grism wheel and a mask slide on each side.  By analogy with
+ * the filter wheel names:
+ *  ID     - numeric position number of wheel or slide
+ *  <null> - text description of filter mounted in that position
+ *  SLID   - the grism wheels and mask slides have codes, this is the
+ *           number of the installed wheel or slide
+ */
+MK_INT( 4101.0, GRISMID ,   "grism position"                                  )
+MK_STR( 4102.0, GRISM   ,   "grism description"                               )
+MK_INT( 4103.0, GRISSLID,   "grism drawer number"                             )
+MK_INT( 4111.0, MASKID  ,   "mask position"                                   )
+MK_STR( 4112.0, MASK    ,   "mask description"                                )
+MK_INT( 4113.0, MASKSLID,   "mask slider number"                              )
+
+/* 4200 through 4299 are GECKO */
+
+/*
+ * Description of the grating installed
+ */
+MK_STR( 4201.0, DISPEL  ,   "grating description"                             )
+/*
+ * Dispersion axis used, always 2 now
+ */
+MK_INT( 4202.0, DISPAXIS,   "dispersion axis (1 or 2)"                        )
+/*
+ * Incidence angle
+ */
+MK_FLT( 4203.0, DISPANG ,6, "grating angle in degrees"                        )
+/*
+ * Resulting center wavelength
+ */
+MK_FLT( 4204.0, WAVELENG,10,"central wavelength in angstroms"                 )
+/*
+ * In this order
+ */
+MK_INT( 4205.0, ORDER   ,   "spectral order"                                  )
+/*
+ * Magic number
+ */
+MK_FLT( 4206.0, GRSETUP ,5, "grating setup constant in degrees"               )
+/*
+ * Another magic number
+ */
+MK_FLT( 4207.0, OPENANG2,7, "setup const in degrees (opening angle / 2)"      )
+/*
+ * Why "grism angle" if it's the angle between 2 prisms ???
+ */
+MK_FLT( 4208.0, GRISMANG,5, "rel.angle between prisms in degrees"             )
+
+/*
+ * unimplemented description of slicer used
+ */
+MK_STR( 4210.0, SLICER  ,   "image slicer in place"                           )
+
+/*
+ * OPEN or CLOSED for each hartman mask
+ */
+MK_STR( 4221.0, HART1A  ,   "hartman mask 1a position OPEN/CLOSED"            )
+MK_STR( 4221.1, HART1B  ,   "hartman mask 1b position OPEN/CLOSED"            )
+MK_STR( 4222.0, HART2   ,   "hartman mask 2 position OPEN/CLOSED"             )
+MK_STR( 4223.0, HART3   ,   "hartman mask 3 position OPEN/CLOSED"             )
+MK_STR( 4224.0, HART4   ,   "hartman mask 4 position OPEN/CLOSED"             )
+
+/*
+ * unimplemented description of train used
+ * could be UV/RED/CAFE
+ */
+MK_STR( 4230.0, COUDETRN,   "coude train color UV/RED/CAFE"                   )
+
+/*
+ * unimplemented exposure meter info
+ */
+MK_STR( 4240.0, EMFILTER,   "exposure meter filter description"               )
+MK_INT( 4241.0, EMCNTS  ,   "exposure meter counts at end"                    )
+MK_INT( 4242.0, MIDEXPTM,   "mid-exposure time (seconds)"                     )
+
+/*
+ * CAFE mode added an additional card indicating that the Coude train was
+ * not being used - this ought to be added to Gecko files with NOTUSED
+ * as the value
+ */
+MK_STR( 4290.0, CAFE    ,   "CAFE is being used Null/INUSE"                   )
+
+
+/* 4300 through 4399 are for Fabre-Perot etalons */
+
+/*
+ * Description of the etalon in use
+ */
+MK_STR( 4301.0, AUXINST ,   "fabry perot etalon description"                  )
+/*
+ * Number used to figure something
+ */
+MK_PFL( 4302.0, CONST   ,2, "fabry perot constant"                            )
+/*
+ * Number of scan positions
+ */
+MK_INT( 4303.0, NUMCHAN ,   "number of channels per scan"                     )
+/*
+ * Current scan position
+ */
+MK_INT( 4304.0, CURCHAN ,   "current channel"                                 )
+/*
+ * Actual control value sent to etalon for this position
+ */
+MK_INT( 4305.0, BINVAL  ,   "binary control value"                            )
+
+/*
+ * GriF needed a bunch more values so some of these are duplicates :-(
+ */
+/*
+ * GriF adds a Fabre-Perot etalon between AOB and KIR.  This value indicates
+ * the position of the slide holding the etalon.
+ */
+MK_STR( 4320.0, GRFPPOS ,   "GriF position of FP carriage - In/Out"           )
+/*
+ * Etalon calibration values, order and standard wavelength, and BCV at
+ * standard wavelength
+ */
+MK_FLT( 4321.1, GRWAVORD,6, "GriF wavelength of order used to scan"           )
+MK_FLT( 4321.2, GRWAVCAL,6, "GriF wavelength of calibration spectral line"    )
+MK_FLT( 4321.3, GRBCVCAL,6, "GriF BCV at calibration line"                    )
+/*
+ * This records what the driver for the scan is
+ *   bcv      - specific BCV positions given
+ *   wave     - a wavelength range given and BCV's calculated
+ *   velocity - a range of delta velocities given and BCV's calculated
+ */
+MK_STR( 4322.0, GRSCANTY,   "GriF type of scan - bcv, wave, velocity"         )
+/*
+ * Wavelength values for scan, beginning, delta, and current
+ */
+MK_FLT( 4323.1, GRWAVBEG,6, "GriF wavelength at beginning of scan"            )
+MK_FLT( 4323.2, GRWAVSTP,6, "GriF wavelength step"                            )
+MK_FLT( 4323.3, GRWAVCUR,6, "GriF current observed wavelength"                )
+/*
+ * BCV equivalents (well, these are calculated from the desired wavelengths
+ * initially, but since the etalon is integral, the current wavelength value
+ * above is a conversion back from the BCV position)
+ */
+MK_FLT( 4324.1, GRBCVBEG,6, "GriF Binary Contol Value at beginning of scan"   )
+MK_FLT( 4324.2, GRBCVSTP,6, "GriF BCV step"                                   )
+MK_INT( 4324.3, GRBCVCUR,   "GriF applied BCV"                                )
+/*
+ * The scan itself is a sequence of etalon positions rounded from the
+ * desired wavelength range and step.  These are the count and current
+ * position in the scan.  (How is GRCURRCH different from GRBCVCUR ??? )
+ */
+MK_INT( 4325.1, GRNBCHAN,   "GriF number of channels in scan"                 )
+MK_INT( 4325.2, GRCURRCH,   "GriF current channel"                            )
+/*
+ * GriF also controls the plate parallelism.  These record the x and y
+ * commands
+ */
+MK_INT( 4326.1, GRBCV_X ,   "GriF X Binary Control Value FP"                  )
+MK_INT( 4326.2, GRBCV_Y ,   "GriF Y Binary Control Value FP"                  )
+
+/*
+ * Environment : 9000.000 to 9999.999
+ *
+ *TYPE --IDX--  KEYWORD- PRC -------------------COMMENT----------------------*/
+/*
+ * Data logger probe readings.  NOTE: The first two columns of the comment
+ * for these make up the authoritative probe numbers.  "loggerh" MUST BE
+ * RECOMPILED to have these changes take effect.  It takes whatever is
+ * currently in the first two columns of the COMMENT and adds /p/logger/.
+ */
+MK_PFL( 9101.0, TESPMIRE,2, "03 temp, surface, primary mirror east deg C"     )
+MK_PFL( 9102.0, TESPMIRW,2, "02 temp, surface, primary mirror west deg C"     )
+MK_PFL( 9103.0, TESPMIRS,2, "01 temp, surface, primary mirror west side degC" )
+MK_PFL( 9104.0, TEAPMCLW,2, "54 temp, air, primary mirror cell west deg C"    )
+MK_PFL( 9105.0, TEAMIRCI,2, "58 temp, air, mirror cooling in at unit deg C"   )
+MK_PFL( 9106.0, TEAMIRCO,2, "27 temp, air, mirror cooling out at cell deg C"  )
+MK_PFL( 9107.0, TEAPMSPN,2, "65 temp, air, mirror spigot north cass deg C"    )
+MK_PFL( 9108.0, TEAPMSPS,2, "64 temp, air, mirror spigot nouth M3 deg C"      )
+MK_PFL( 9109.0, TEATRNGE,2, "23 temp, air, top ring east deg C"               )
+MK_PFL( 9110.0, TEATRNGW,2, "06 temp, air, top ring west deg C"               )
+MK_PFL( 9111.0, TEANRLSB,2, "19 temp, air, north rail support beam deg C"     )
+MK_PFL( 9112.0, TESHRSET,2, "08 temp, surface, horseshoe east top deg C"      )
+MK_PFL( 9113.0, TESTELTL,2, "49 temp, surface, telescope truss low deg C"     )
+MK_PFL( 9114.0, TESTELTH,2, "52 temp, surface, telescope truss high deg C"    )
+MK_PFL( 9115.0, TEALOWWS,2, "38 temp, air, dome lower weather stat side degC" )
+MK_PFL( 9116.0, TEATOPWS,2, "36 temp, air, dome top weather stat side deg C"  )
+MK_PFL( 9117.0, TEATOPOP,2, "37 temp, air, dome top opposite weath side degC" )
+MK_PFL( 9118.0, TEA2INCH,2, "45 temp, air, two inches above fifth floor degC" )
+MK_PFL( 9119.0, TEA2INEB,2, "53 temp, air, two inches up by electronics degC" )
+MK_PFL( 9120.0, TEA6FOOT,2, "43 temp, air, six feet above fifth floor deg C"  )
+MK_PFL( 9121.0, TESCONRM,2, "61 temp, surface, floor above control room degC" )
+MK_PFL( 9122.0, TESPIERN,2, "59 temp, surface, floor by north pier deg C"     )
+MK_PFL( 9123.0, TESPIERS,2, "60 temp, surface, floor by south pier deg C"     )
+MK_PFL( 9124.0, TEAWTHRT,2, "35 temp, air, weathertron deg C"                 )
+MK_PFL( 9125.0, TEMPERAT,2, "86 temp, air, weather tower deg C"               )
+MK_PFL( 9126.0, WINDSPED,2, "84 wind speed, weather tower knots"              )
+MK_PFL( 9127.0, WINDDIR ,2, "85 wind direction, weather tower deg (N=0 E=90)" )
+MK_PFL( 9128.0, RELHUMID,2, "87 relative humidity, weather tower %"           )
+MK_PFL( 9129.0, PRESSURE,2, "31 barometric pressure, control room mb"         )
+
+/*
+ * Queue Scheduled Observing : 11000.000 to 11999.999
+ *
+ *TYPE --IDX--  KEYWORD- PRC -------------------COMMENT----------------------*/
+
+/*
+ * Processing Pipeline : 15000.000 to 15999.999
+ *
+ *TYPE --IDX--  KEYWORD- PRC -------------------COMMENT----------------------*/
+
+MK_CMT(15000.1, CMTPROC1,   ""                                                )
+MK_CMT(15000.2, CMTPROC2,   "Processing Pipeline"                             )
+MK_CMT(15000.3, CMTPROC3,   "-------------------"                             )
+MK_CMT(15000.4, CMTPROC4,   ""                                                )
+MK_STR(15101.0, CRUNID,     "Elixir camera run ID"                            )
+/*
+ * Archive System : 90000.000 to 99999.999
+ *
+ *TYPE --IDX--  KEYWORD- PRC -------------------COMMENT----------------------*/
+
+#endif /* !_INCLUDED_fh_registry */
Index: /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfhreg/macros.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfhreg/macros.h	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/fits/libfhreg/macros.h	(revision 23594)
@@ -0,0 +1,145 @@
+/*                                             -*- c-file-style: "Ellemtel" -*-
+
+`macros.h' - CPP macros to make fhreg work, automatically included
+
+This file is part of version 0 of the FITS Keyword Registry.
+Read the `License' file for terms of use and distribution.
+Copyright 2004, Pan-STARRS, isani@ifa.hawaii.edu.
+
+___This header automatically generated from `Index'. Do not edit it here!___ */
+
+/*
+ * Documentation: See http://software.cfht.hawaii.edu/fh_registry/
+ *
+ * === NOTE: That only describes the CFHT version of the registry!
+ */
+
+#ifndef _INCLUDED_fhreg_macros
+#define _INCLUDED_fhreg_macros 1
+
+/*
+ * This macro is used inside the other macros (ID_FLT, ID_INT, etc.)
+ * so that the keyword names can be referred to as fh_kw_FOO
+ * This can be used as an identifier to fh_get() or any other function
+ * instead of using the string constant "FOO".
+ */
+
+#define ID_FLT(idx, name, keyword, prec, comment) \
+static inline void \
+fh_set_##name (HeaderUnit hu, double value) \
+{ fh_set_flt(hu, idx, keyword, value, prec, comment); } \
+static inline void \
+fh_setval_##name (HeaderUnit hu, const char* value) \
+{ fh_set_val(hu, idx, keyword, value, comment); } \
+static inline void \
+fh_setcmt_##name (HeaderUnit hu, double value, const char* new_comment) \
+{ fh_set_flt(hu, idx, keyword, value, prec, new_comment); } \
+static inline fh_result \
+fh_get_##name (HeaderUnit hu, double* value) \
+{ return fh_get_flt(hu, keyword, value); } \
+static inline const char* \
+fh_kw_##name(void) { return keyword; } \
+static inline const char* \
+fh_cmt_##name(void) { return comment; }
+
+#define ID_PFL(idx, name, keyword, prec, comment) \
+static inline void \
+fh_set_##name (HeaderUnit hu, double value) \
+{ fh_set_pfl(hu, idx, keyword, value, prec, comment); } \
+static inline void \
+fh_setval_##name (HeaderUnit hu, const char* value) \
+{ fh_set_val(hu, idx, keyword, value, comment); } \
+static inline void \
+fh_setcmt_##name (HeaderUnit hu, double value, const char* new_comment) \
+{ fh_set_pfl(hu, idx, keyword, value, prec, new_comment); } \
+static inline fh_result \
+fh_get_##name (HeaderUnit hu, double* value) \
+{ return fh_get_flt(hu, keyword, value); } \
+static inline const char* \
+fh_kw_##name(void) { return keyword; } \
+static inline const char* \
+fh_cmt_##name(void) { return comment; }
+
+#define ID_INT(idx, name, keyword, comment) \
+static inline void \
+fh_set_##name (HeaderUnit hu, int value) \
+{ fh_set_int(hu, idx, keyword, value, comment); } \
+static inline void \
+fh_setval_##name (HeaderUnit hu, const char* value) \
+{ fh_set_val(hu, idx, keyword, value, comment); } \
+static inline void \
+fh_setcmt_##name (HeaderUnit hu, int value, const char* new_comment) \
+{ fh_set_int(hu, idx, keyword, value, new_comment); } \
+static inline fh_result \
+fh_get_##name (HeaderUnit hu, int* value) \
+{ return fh_get_int(hu, keyword, value); } \
+static inline const char* \
+fh_kw_##name(void) { return keyword; } \
+static inline const char* \
+fh_cmt_##name(void) { return comment; }
+
+#define ID_BLN(idx, name, keyword, comment) \
+static inline void \
+fh_set_##name (HeaderUnit hu, fh_bool value) \
+{ fh_set_bool(hu, idx, keyword, value, comment); } \
+static inline void \
+fh_setval_##name (HeaderUnit hu, const char* value) \
+{ fh_set_val(hu, idx, keyword, value, comment); } \
+static inline void \
+fh_setcmt_##name (HeaderUnit hu, fh_bool value, const char* new_comment) \
+{ fh_set_bool(hu, idx, keyword, value, new_comment); } \
+static inline fh_result \
+fh_get_##name (HeaderUnit hu, fh_bool* value) \
+{ return fh_get_bool(hu, keyword, value); } \
+static inline const char* \
+fh_kw_##name(void) { return keyword; } \
+static inline const char* \
+fh_cmt_##name(void) { return comment; }
+
+#define ID_STR(idx, name, keyword, comment) \
+static inline void \
+fh_set_##name (HeaderUnit hu, const char* value) \
+{ fh_set_str(hu, idx, keyword, value, comment); } \
+static inline void \
+fh_setval_##name (HeaderUnit hu, const char* value) \
+{ fh_set_val(hu, idx, keyword, value, comment); } \
+static inline void \
+fh_setcmt_##name (HeaderUnit hu, const char* value, const char* new_comment) \
+{ fh_set_str(hu, idx, keyword, value, new_comment); } \
+static inline fh_result \
+fh_get_##name (HeaderUnit hu, char* value, int maxlen) \
+{ return fh_get_str(hu, keyword, value, maxlen); } \
+static inline const char* \
+fh_kw_##name(void) { return keyword; } \
+static inline const char* \
+fh_cmt_##name(void) { return comment; }
+
+#define ID_VAL(idx, name, keyword, comment) \
+static inline void \
+fh_set_##name (HeaderUnit hu, const char* value) \
+{ fh_set_val(hu, idx, keyword, value, comment); } \
+static inline void \
+fh_setcmt_##name (HeaderUnit hu, const char* value, const char* new_comment) \
+{ fh_set_val(hu, idx, keyword, value, new_comment); } \
+static inline const char* \
+fh_kw_##name(void) { return keyword; } \
+static inline const char* \
+fh_cmt_##name(void) { return comment; }
+
+#define ID_CMT(idx, name, comment) \
+static inline void \
+fh_set_##name (HeaderUnit hu) \
+{ fh_set_com(hu, idx, "COMMENT", comment); }
+
+/*
+ * Now the auto-stringified versions of ID_*() ...
+ */
+#define MK_FLT(idx, name, dig, comment) ID_FLT(idx, name, #name, dig, comment)
+#define MK_PFL(idx, name, dec, comment) ID_PFL(idx, name, #name, dec, comment)
+#define MK_INT(idx, name, comment) ID_INT(idx, name, #name, comment)
+#define MK_STR(idx, name, comment) ID_STR(idx, name, #name, comment)
+#define MK_VAL(idx, name, comment) ID_VAL(idx, name, #name, comment)
+#define MK_BLN(idx, name, comment) ID_BLN(idx, name, #name, comment)
+#define MK_CMT(idx, name, comment) ID_CMT(idx, name, comment)
+
+#endif /* !_INCLUDED_fhreg_macros */
Index: /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/missing_protos.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/missing_protos.h	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/extsrc/gpcsw/gpcsrc/missing_protos.h	(revision 23594)
@@ -0,0 +1,327 @@
+/* Copyright (C) 1997-2002  Canada-France-Hawaii Telescope Corp.         */
+/* This program is distributed WITHOUT any warranty, and is under the    */
+/* terms of the GNU General Public License, see the file COPYING         */
+/*****************************************************************************
+ *
+ * missing_protos.h - ANSI prototypes for systems that lack complete ANSI
+ *                    system header files (especially SunOS 4.x).  This file
+ *                    should be included with `#include <missing_protos.h>'
+ *                    from "cfht/cfht.h" so that it does not show up in
+ *                    the dependencies generated by "gcc -MM".  Proper ANSI
+ *                    systems should not need anything from this file anyway,
+ *                    and thus will not be tempted to re-compile everything
+ *                    which depends on this file every time we need to add
+ *                    a new prototype for the SunOS machines.
+ * $Id: missing_protos.h,v 1.15 2002/08/26 22:22:32 thomas Exp $
+ * $Locker:  $
+ *
+ * $Log: missing_protos.h,v $
+ * Revision 1.15  2002/08/26 22:22:32  thomas
+ * Added VxWorks to "Make.Common is not being used" #if collection
+ *
+ * Revision 1.14  2001/12/05 00:56:22  thomas
+ * Added mknod(2) for SunOS
+ *
+ * Revision 1.13  2001/12/05 00:13:28  thomas
+ * Added sys/file.h and flock for SunOS
+ *
+ * Revision 1.12  2001/10/14 20:28:32  isani
+ * optarg stuff for SUNOS.
+ * Missing socklen_t on HP 10.20.
+ *
+ * Revision 1.11  2000/02/10 13:52:23  isani
+ * Added a few more prototypes.
+ * Changed to new way of doing SELECT() call.
+ *
+ * Revision 1.10  1999/07/30 22:14:51  thomas
+ * HP added openlog/syslog in 10.X, if it out there
+ *
+ * Revision 1.9  1999/07/13 21:27:28  thomas
+ * Added WCOREDUMP for SunOS
+ *
+ * Revision 1.8  1999/07/13 00:17:24  thomas
+ * Fix 1.7, Sidik's model was not sufficient, plus GNU rcs would not let me
+ * cancel it ???
+ *
+ * Revision 1.7  1999/07/03 04:10:58  thomas
+ * Changed select for 10.20
+ *
+ * Revision 1.6  98/07/10  10:38:35  10:38:35  thomas (Jim Thomas)
+ * Added perror, strerror, memset, drand48 to SunOS prototypes
+ * 
+ * Revision 1.5  97/12/18  21:28:43  21:28:43  thomas (Jim Thomas)
+ * Put floatingpoint.h back in, added strtol, changed __STDC__ to __GNUC__
+ * 
+ * Revision 1.4  97/12/18  02:38:20  02:38:20  isani (Sidik Isani)
+ * Added redefinition of CFHT_SIG_{DFL,IGN} for SunOS
+ * 
+ * Revision 1.3  1997/12/05 20:49:44  thomas
+ * Added definition of const if not ANSI C
+ *
+ * Revision 1.2  97/12/05  20:20:44  20:20:44  jcc (Jean-Charles Cuillandre)
+ * Added include of resource.h
+ * 
+ * Revision 1.1  1997/11/23 11:29:04  isani
+ * Initial revision
+ *
+ ****************************************************************************/
+
+#ifndef _INCLUDED_missing_protos
+#define _INCLUDED_missing_protos 1
+
+/*
+ * Make.Common defines *one* of the following:
+ *             SUNOS, SOLARIS, HPUX, LINUX, VXWORKS, or CYGWIN32
+ * We will use these here, but for projects where Make.Include is used
+ * instead of Make.Common, check for some other predefined symbols like
+ * __sun__ to try to figure out the system type.
+ */
+
+#if defined(SunOS) && !defined(SUNOS)
+#define SUNOS
+#endif
+
+#if (defined(Linux) || defined(linux)) && !defined(LINUX)
+#define LINUX
+#endif
+
+/*
+ * First, make sure SUNOS didn't get defined by accident for a Solaris box.
+ * We use SOLARIS instead, because they are really different OS's.
+ */
+
+#if defined(SUNOS) && defined(__svr4__)
+#undef SUNOS
+#ifndef SOLARIS
+#define SOLARIS
+#endif
+#endif
+
+#if !defined(SUNOS) && !defined(SOLARIS) && !defined(HPUX) && !defined(LINUX) && !defined(CYGWIN32) && !defined(VXWORKS)
+/*
+ * If Make.Common isn't being used ...
+ */
+#if defined(__sun__) || defined(sun)
+#ifndef sun
+#define sun
+#endif
+#ifdef __svr4__
+#define SOLARIS
+#else
+#define SUNOS
+#endif
+#else
+#if defined(__hpux__) || defined(hpux)
+#ifndef hpux
+#define hpux
+#endif
+#define HPUX
+#else
+#if defined(vxWorks)
+#define VXWORKS
+#else
+error: Unknown system type!
+error: This software has not been tested on this platform.
+error: (comment out this block to compile anyway).
+#endif /* vxWorks */
+#endif /* __hpux__ */
+#endif /* __sun__ */
+#endif /* !SUNOS && !SOLARIS && !HPUX ... */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* "const" has been added to a bunch of definitions, but that is ANSI :-( */
+#ifndef __STDC__
+#define const
+#endif
+
+/*
+ * Here are the hacks for the old suns, so we don't have to include them
+ * in each source file.  Solaris 2.x does not need any of this stuff...
+ */
+
+#ifdef SUNOS
+#define mktime timelocal
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <sys/sem.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <sys/resource.h>
+#ifndef WCOREDUMP
+#define WCOREDUMP(n) 0
+#endif
+#include <malloc.h>
+#include <unistd.h>
+#ifdef __GNUC__
+#include <float.h>             /* for DBL_MIN and DBL_MAX */
+#else
+#include <values.h>
+#ifndef DBL_MIN
+#define DBL_MIN MINDOUBLE
+#define DBL_MAX MAXDOUBLE
+#endif
+#endif /* !__GNUC__ */
+#ifdef __GNUC__
+void    perror(const char *s);
+char	*strerror(int);
+int     ioctl(int, int, ...);
+int	sync(void);
+int     select(int, fd_set*, fd_set*, fd_set*, struct timeval*);
+/* bzero is not ANSI, but the FD_ macros for select need it, so we need a
+ * proto here to prevent warnings.  Use memset() in your own code instead!
+ */
+void    bzero(char*, int);
+void*   memset(void*, int, size_t);
+int     socket(int, int, int);
+int     bind(int,  struct sockaddr*, int);
+int     listen(int, int);
+int     connect(int, struct sockaddr*, int);
+int     accept(int, struct sockaddr*, int*);
+int     shutdown(int, int);
+int     getsockname(int,struct sockaddr*,int*);
+int     getpeername(int,struct sockaddr*,int*);
+int     setsockopt(int,int,int,const void*,int);
+int     socketpair(int,int,int,int [2]);
+int     getrlimit(int,struct rlimit*);
+int     setrlimit(int,const struct rlimit*);
+int     gettimeofday(struct timeval *tp, struct timezone *tzp);
+int     rename(const char*, const char*);
+int     symlink(const char*, const char*);
+int     readlink(const char*, char*, int);
+int     lstat(const char*, struct stat*);
+int	mknod(char *, int, int);
+int     openlog(const char*, int, int);
+int     syslog(int, const char*, ...);
+int     getitimer(int, struct itimerval*);
+int     setitimer(int, const struct itimerval*, struct itimerval*);
+int	fchmod(int, int);
+int	getdtablesize(void);
+int	semop(int, struct sembuf*, unsigned int);
+key_t	ftok(const char *, int);
+int	semget(key_t, int, int);
+int	lockf(int, int, off_t);
+int	flock(int, int);
+int	ftruncate(int, off_t);
+/* %%% The protos for CFHT_SIG_DFL et al are defined in terms of
+ *     SIG_DFL et al defined in signal.h, but unfortunately these
+ *     are not properly prototyped either :-(  Overriding them here
+ *     is kind of dangerous, and should be checked carefully.
+ */
+#undef  CFHT_SIG_DFL
+#undef  CFHT_SIG_IGN
+#define CFHT_SIG_DFL ((PFV)(0))
+#define CFHT_SIG_IGN ((PFV)(1))
+/*
+ * C library contains these (there is NO optopt in SUNOS, but its getopt also
+ * never returns ':'.  Instead, it prints its own message about missing args.
+ */
+extern char *optarg;
+extern int optind, opterr;
+#else  /* not __GNUC__ */
+#include <floatingpoint.h>
+void perror();
+char *strerror();
+void *memset();
+double drand48();
+long strtol();
+#endif /* __GNUC__ */
+#endif /* SUNOS */
+
+/*
+ * Here are the hacks that are also needed on the newer Solaris 2.x machines.
+ */
+#if defined(SUNOS) || defined(SOLARIS)
+#include <unistd.h>
+#ifdef __STDC__
+int	gethostname(char*, int);
+int     seteuid(uid_t euid);
+int     setegid(gid_t egid);
+int	setreuid(uid_t, uid_t);
+int	setregid(gid_t, gid_t);
+char*   crypt(const char*, const char*);
+#endif
+extern char** environ;
+#endif
+
+/*
+ * HPUX has some quirks too.
+ */
+#ifdef HPUX
+#if defined(OSVersion) && (OSVersion < 10)
+/*
+ * Make.Common defines HACK_SELECT directly and doesn't use this.
+ * See #if/not HACK_SELECT below also.
+ */
+#define HACK_SELECT
+#endif
+
+/* HP 10.20 seems not to define socklen_t */
+
+#define socklen_t int
+
+/* See comments above under SUNOS */
+#undef  CFHT_SIG_DFL
+#undef  CFHT_SIG_IGN
+#define CFHT_SIG_DFL ((PFV)(0))
+#define CFHT_SIG_IGN ((PFV)(1))
+
+#define seteuid(euid) setresuid(-1,(euid),-1)
+#define setegid(egid) setresgid(-1,(egid),-1)
+#ifdef __STDC__
+/* HP-UX 9 needed these.  Shouldn't hurt under 10.20 as long as they match. */
+void openlog(const char *, int, int);
+void syslog(int, const char *, ...);
+#endif
+#else /* not HP */
+#ifdef __STDC__
+/* only HP provides a prealloc function */
+int	prealloc(int fildes, unsigned size);
+#endif
+#endif
+
+/*
+ * CYGWIN32 is still in beta, so probably much of this stuff
+ * will need to be removed once this compiler stabilizes.
+ */
+#ifdef CYGWIN32
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#define seteuid(x) (0)
+#define setegid(x) (0)
+#define setuid(x)  (0)
+#define setgid(x)  (0)
+int readlink(const char*, char*, int);
+int select(int,fd_set*,fd_set*,fd_set*,struct timeval*);
+int lstat(const char*, struct stat*);
+int gettimeofday(struct timeval*, struct timezone*);
+const char* crypt(const char*, const char*);
+#endif
+
+/*
+ * Before version 10, HP defines fd_set, but doesn't use it in the prototype
+ * for select().  Also the count is size_t (which is right, why did ANSI
+ * lose that one?  I guess because C does not really support types :-( ).
+ * So do select() like this to avoid warnings on HP
+ *
+ *   Status = SELECT(n, read, write, error, &timestruct);
+ */
+#ifdef HACK_SELECT
+#define SELECT(n, read, write, error, tp)        \
+        select((size_t) (n), (int *) (read),     \
+               (int *) (write), (int *) (error), \
+	       (tp))
+#else
+/* Let the compiler check against a real prototype. */
+#define SELECT select
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* !_INCLUDED_missing_protos */
Index: /branches/cnb_branches/cnb_branch_20090301/glueforge/templates/psdb/configure_ac.tt
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/glueforge/templates/psdb/configure_ac.tt	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/glueforge/templates/psdb/configure_ac.tt	(revision 23594)
@@ -40,5 +40,4 @@
 CFLAGS=${TMP_CFLAGS}
 
-
 AC_PATH_PROG([PERL], [perl], [missing])
 if test "$PERL" = "missing" ; then
@@ -46,5 +45,9 @@
 fi
 
-AC_PATH_PROG([DOXYGEN], [doxygen], [])
+AC_ARG_ENABLE(doxygen,
+              [AS_HELP_STRING(--enable-doxygen,doxygen-erated documentation)],
+              [AC_MSG_RESULT(doxygen enabled)
+               AC_PATH_PROG([DOXYGEN], [doxygen], [])],
+              [AC_MSG_RESULT(doxygen disabled)])
 AM_CONDITIONAL([HAVE_DOXYGEN], [test -n "$DOXYGEN"])
 
Index: /branches/cnb_branches/cnb_branch_20090301/ippMonitor/raw/ipp.php
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippMonitor/raw/ipp.php	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippMonitor/raw/ipp.php	(revision 23594)
@@ -40,5 +40,5 @@
 
   // make this a DB lookup
-  $sql = "SELECT projname FROM projects";
+  $sql = "SELECT projname FROM projects order by projname";
 
   $qry = $db->query($sql);
Index: /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/chip_imfile.pl
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/chip_imfile.pl	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/chip_imfile.pl	(revision 23594)
@@ -35,7 +35,9 @@
 }
 
+my @ARGS = @ARGV;
+
 # Parse the command-line arguments
 my ( $exp_id, $chip_id, $class_id, $chip_imfile_id, $uri, $camera, $outroot, $dbname, $run_state, $reduction, $threads, $verbose,
-     $no_update, $no_op, $redirect );
+     $no_update, $no_op, $redirect, $magicked, $deburned );
 GetOptions(
     'exp_id=s'          => \$exp_id,    # Exposure identifier
@@ -49,4 +51,6 @@
     'reduction=s'       => \$reduction, # Reduction class
     'run-state=s'       => \$run_state, # current state of the run (new, update)
+    'magicked'          => \$magicked,  # magicked state of input file
+    'deburned=s'        => \$deburned,  # does deburned image exist? 
     'threads=s'         => \$threads,   # Number of threads to use for ppImage
     'verbose'           => \$verbose,   # Print to stdout
@@ -77,5 +81,5 @@
     print STDOUT "\n\n";
     print STDOUT "Starting script $0 on $host\n\n";
-    print STDOUT "COMMAND IS: @ARGV\n\n";
+    print STDOUT "FULL COMMAND: $0 @ARGS\n\n";
 }
 
@@ -123,4 +127,22 @@
     my $command;
     my $do_stats;
+
+    ## get the ppImage recipe for this camera and CHIP reduction
+    $command = "$ppConfigDump -camera $camera -dump-recipe PPIMAGE -recipe PPIMAGE $recipe_ppImage -";
+    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 ppConfigDump: $error_code", $exp_id, $chip_id, $class_id, $PS_EXIT_SYS_ERROR);
+    }
+    my $recipeData = $mdcParser->parse(join "", @$stdout_buf) or
+        &my_die("Unable to parse metadata config doc", $exp_id, $chip_id, $class_id, $PS_EXIT_SYS_ERROR);
+
+    ## XXX make the feature more general?
+    my $useDeburnedImage = metadataLookupBool($recipeData, 'USE.DEBURNED.IMAGE');
+    if ($useDeburnedImage && $deburned) {
+	$uri =~ s/fits$/burn.fits/;
+	&my_die("Couldn't find deburned input file: $uri\n", $exp_id, $chip_id, $class_id, $PS_EXIT_SYS_ERROR) unless $ipprc->file_exists($uri);
+    }
 
     if ($run_state eq "new") {
@@ -150,5 +172,5 @@
     }
 
-    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+    ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
         run(command => $command, verbose => $verbose);
     unless ($success) {
@@ -156,15 +178,4 @@
         &my_die("Unable to perform ppImage: $error_code", $exp_id, $chip_id, $class_id, $error_code);
     }
-
-    ## get the ppImage recipe for this camera and CHIP reduction
-    $command = "$ppConfigDump -camera $camera -dump-recipe PPIMAGE -recipe PPIMAGE $recipe_ppImage -";
-    ( $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 ppConfigDump: $error_code", $exp_id, $chip_id, $class_id, $PS_EXIT_SYS_ERROR);
-    }
-    my $recipeData = $mdcParser->parse(join "", @$stdout_buf) or
-        &my_die("Unable to parse metadata config doc", $exp_id, $chip_id, $class_id, $PS_EXIT_SYS_ERROR);
 
     ## allow the output images to be optional, depending on the recipe / reduction class
@@ -215,4 +226,5 @@
     $command .= " -uri $outputImage";
     $command .= " -path_base $outroot";
+    $command .= " -magicked" if $magicked;
     $command .= " -hostname $host" if defined $host;
     $command .= " -dbname $dbname" if defined $dbname;
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 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/detrend_norm_apply.pl	(revision 23594)
@@ -89,4 +89,5 @@
     'darkmask'         => 'mask',
     'flatmask'         => 'mask',
+    'ctemask'          => 'mask',
     };
 
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 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/detrend_norm_calc.pl	(revision 23594)
@@ -81,4 +81,5 @@
     'darkmask'         => 0,
     'flatmask'         => 0,
+    'ctemask'          => 0,
     };
 
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 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/detrend_process_imfile.pl	(revision 23594)
@@ -79,4 +79,5 @@
                   'DARK'             => undef,
                   'DARK_PREMASK'     => undef,
+                  'CTEMASK'          => undef,
                   'SHUTTER'          => 'PPIMAGE.OUTPUT.DETREND',
                   'FLAT_PREMASK'     => 'PPIMAGE.OUTPUT.DETREND',
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 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/detrend_resid_exp.pl	(revision 23594)
@@ -124,4 +124,6 @@
     # 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
+    # Correcting the resid imfiles and stats requires us to *divide* by the normalization
+    # we do this by inverting the normalization here:
     my $normsFile;
     ($normsFile, $normsName) = tempfile( "/tmp/$exp_tag.detresid.$det_id.$iter.norms.XXXX", UNLINK => !$save_temps );
@@ -129,5 +131,5 @@
     foreach my $norm (@$normsMD) {
         my $class_id = $norm->{class_id};
-        my $normalization = $norm->{norm};
+	my $normalization = 1.0 / $norm->{norm};
 
         $norms{$class_id} = $normalization;
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 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/detrend_resid_imfile.pl	(revision 23594)
@@ -114,4 +114,5 @@
     'darkmask'         => '-mask',      # Specify the mask frame
     'flatmask'         => '-mask',      # Specify the mask frame
+    'ctemask'          => '-mask',      # Specify the mask frame
 };
 
@@ -119,4 +120,5 @@
 my $FILERULES = { 'FLATMASK'         => 'PPIMAGE.OUTPUT.RESID',
                   'DARKMASK'         => 'PPIMAGE.OUTPUT.RESID',
+                  'CTEMASK'          => 'PPIMAGE.OUTPUT.RESID',
                   'MASK'             => 'PPIMAGE.OUTPUT.RESID',
                   'BIAS'             => 'PPIMAGE.OUTPUT.RESID',
Index: /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/detrend_stack.pl
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/detrend_stack.pl	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/detrend_stack.pl	(revision 23594)
@@ -75,4 +75,5 @@
 my $FILERULES = { 'FLATMASK'         => 'PPMERGE.OUTPUT.MASK',
                   'DARKMASK'         => 'PPMERGE.OUTPUT.MASK',
+                  'CTEMASK'          => 'PPMERGE.OUTPUT.MASK',
                   'MASK'             => 'PPMERGE.OUTPUT.MASK',
                   'BIAS'             => 'PPMERGE.OUTPUT.BIAS',
@@ -97,4 +98,5 @@
 my $STATRECIPES = {'FLATMASK'         => 'DETSTATS',
                    'DARKMASK'         => 'DETSTATS',
+                   'CTEMASK'          => 'DETSTATS',
                    'MASK'             => 'DETSTATS',
                    'BIAS'             => 'DETSTATS',
Index: /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/diff_skycell.pl
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/diff_skycell.pl	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/diff_skycell.pl	(revision 23594)
@@ -91,8 +91,10 @@
 
 # Identify the input and the template
-my ($input, $inputMask, $inputVariance, $inputPath); # Input files and path
+my ($input, $inputMask, $inputVariance, $inputPath, $inputSources); # Input files and path
 my ($template, $templateMask, $templateVariance, $templatePath, $templateSources); # Template files and path
 my $tess_id;                    # Tesselation identifier
 my $camera;                     # Camera
+my $magicked_0;
+my $magicked_1;
 foreach my $file (@$files) {
     if (defined $file->{template} and $file->{template}) {
@@ -103,4 +105,6 @@
             $templateVariance = "PPSTACK.OUTPUT.VARIANCE";
             $templateSources = "PSPHOT.OUT.CMF.MEF";  ## this must be consistent with the value in stack_skycell.pl:161
+            # template is a stack so it doesn't need to be magicked
+            $magicked_1 = 1;
             ## use an explicit stack name for psphot output objects
         } else {
@@ -108,14 +112,18 @@
             $templateVariance = "PSWARP.OUTPUT.VARIANCE";
             $templateSources = "PSWARP.OUTPUT.SOURCES";
+            $magicked_1 = $file->{magicked};
         }
     } else {
         $input = $file->{uri};
         $inputPath = $file->{path_base};
+        $magicked_0 = $file->{magicked};    # if input is a stack the output can't be "magicked"
         if ($file->{warp_id} == 0) {
             $inputMask = "PPSTACK.OUTPUT.MASK";
             $inputVariance = "PPSTACK.OUTPUT.VARIANCE";
+            $inputSources = "PSPHOT.OUT.CMF.MEF";  ## this must be consistent with the value in stack_skycell.pl:161
         } else {
             $inputMask = "PSWARP.OUTPUT.MASK";
             $inputVariance = "PSWARP.OUTPUT.VARIANCE";
+            $inputSources = "PSWARP.OUTPUT.SOURCES";
         }
     }
@@ -144,4 +152,11 @@
 &my_die("Unable to identify camera", $diff_id, $skycell_id, $PS_EXIT_SYS_ERROR) unless defined $camera;
 $ipprc->define_camera($camera);
+
+# Compute the magicked status of the output.
+# The output file will be considered magicked if the input has been magicked and the
+# template is either a stack or a warp that has been magicked.
+# note that difftool -inputskyfile outputs the magicked boolean as an int not T or F
+# because the output is constructed from a union of two selects
+my $magicked = $magicked_0 && $magicked_1;
 
 # Recipes to use based on reduction class
@@ -160,9 +175,12 @@
 # print "inputVariance: $inputVariance\n";
 # print "templateSources: $templateSources\n";
+# print "inputSources: $inputSources\n";
+
+$inputMask = $ipprc->filename($inputMask, $inputPath);
+$inputVariance = $ipprc->filename($inputVariance, $inputPath);
+$inputSources = $ipprc->filename($inputSources, $inputPath);
 
 $templateMask = $ipprc->filename($templateMask, $templatePath);
-$inputMask = $ipprc->filename($inputMask, $inputPath);
 $templateVariance = $ipprc->filename($templateVariance, $templatePath);
-$inputVariance = $ipprc->filename($inputVariance, $inputPath);
 $templateSources = $ipprc->filename($templateSources, $templatePath);
 
@@ -174,12 +192,14 @@
 print "inputVariance: $inputVariance\n";
 print "templateSources: $templateSources\n";
+print "inputSources: $inputSources\n";
 
 &my_die("Couldn't find input: $template", $diff_id, $skycell_id, $PS_EXIT_SYS_ERROR) unless $ipprc->file_exists($template);
 &my_die("Couldn't find input: $templateMask", $diff_id, $skycell_id, $PS_EXIT_SYS_ERROR) unless $ipprc->file_exists($templateMask);
 &my_die("Couldn't find input: $templateVariance", $diff_id, $skycell_id, $PS_EXIT_SYS_ERROR) unless $ipprc->file_exists($templateVariance);
+&my_die("Couldn't find input: $templateSources", $diff_id, $skycell_id, $PS_EXIT_SYS_ERROR) unless $ipprc->file_exists($templateSources);
 &my_die("Couldn't find input: $input", $diff_id, $skycell_id, $PS_EXIT_SYS_ERROR) unless $ipprc->file_exists($input);
 &my_die("Couldn't find input: $inputMask", $diff_id, $skycell_id, $PS_EXIT_SYS_ERROR) unless $ipprc->file_exists($inputMask);
 &my_die("Couldn't find input: $inputVariance", $diff_id, $skycell_id, $PS_EXIT_SYS_ERROR) unless $ipprc->file_exists($inputVariance);
-&my_die("Couldn't find input: $templateSources", $diff_id, $skycell_id, $PS_EXIT_SYS_ERROR) unless $ipprc->file_exists($templateSources);
+&my_die("Couldn't find input: $inputSources", $diff_id, $skycell_id, $PS_EXIT_SYS_ERROR) unless $ipprc->file_exists($inputSources);
 
 # Get the output filenames
@@ -198,9 +218,13 @@
 # Perform subtraction
 unless ($no_op) {
-    my $command = "$ppSub $input $template $outroot";
+    my $command = "$ppSub $outroot";
+    $command .= " -inimage $input";
+    $command .= " -refimage $template";
     $command .= " -inmask $inputMask";
     $command .= " -refmask $templateMask";
     $command .= " -invariance $inputVariance";
     $command .= " -refvariance $templateVariance";
+    $command .= " -insources $inputSources";
+    $command .= " -refsources $templateSources";
     $command .= " -stats $outputStats";
     $command .= " -threads $threads" if defined $threads;
@@ -211,5 +235,4 @@
     $command .= " -F PSPHOT.OUTPUT PSPHOT.OUT.CMF.MEF";
     $command .= " -F PSPHOT.BACKMDL PSPHOT.BACKMDL.MEF";
-    $command .= " -sources $templateSources";
     $command .= " -photometry";
     $command .= " -tracedest $traceDest -log $logDest";
@@ -255,4 +278,5 @@
         my $command = "$difftool -adddiffskyfile -diff_id $diff_id -skycell_id $skycell_id -uri $outputName -path_base $outroot";
         $command .= " $cmdflags";
+        $command .= " -magicked" if $magicked;
         $command .= (" -dtime_script " . ((DateTime->now->mjd - $mjd_start) * 86400));
         $command .= " -hostname $host" if defined $host;
Index: /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/dist_component.pl
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/dist_component.pl	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/dist_component.pl	(revision 23594)
@@ -40,8 +40,8 @@
 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);
+my ($dbname, $save_temps, $verbose, $no_update, $logfile);
 
 GetOptions(
-           'dist_id=s'  => \$dist_id,# Magic destreak run identifier
+           'dist_id=s'      => \$dist_id,    # distribution run identifier
            'camera=s'       => \$camera,     # camera for evaluating file rules
            'stage=s'        => \$stage,      # raw, chip, warp, or diff
@@ -52,12 +52,11 @@
            '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
+           'magicked'       => \$magicked,   # magicked state for this component
            'outroot=s'      => \$outroot,    # "directory" for outputs
-           'clean=s'        => \$clean,      # create clean distribution
+           'clean'          => \$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 );
@@ -86,12 +85,4 @@
 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) {
@@ -99,5 +90,4 @@
     &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
@@ -134,6 +124,12 @@
 foreach my $file (@$file_list) {
     my $base = basename($file);
+    my $path = $ipprc->file_resolve($file);
+    # if file is not found, just skip it.
+    # This is not always the right thing to do.
+    # For example if warpSkyfile is ignored, there won't be many of the data products
+    # the new config dump will only list the files actually produced.
+    next if !$path;
+
     push @base_list, $base;
-    my $path = $ipprc->file_resolve($file);
     symlink $path, "$tmpdir/$base";
 }
@@ -142,8 +138,4 @@
 
     # 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) {
@@ -181,13 +173,9 @@
     $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 ( $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);
     }
 }
@@ -207,13 +195,9 @@
     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";
+    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);
     }
 
@@ -227,8 +211,5 @@
     $bytes = $finfo[7];
 
-    if (!$save_temps) {
-        # delete the temp directory and it's contents
-        system "rm -r $tmpdir";
-    }
+    delete_tmpdir($tmpdir);
 }
 {
@@ -265,10 +246,5 @@
         # 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 $clean_mdc = get_legacy_file_mdc();
 
         my $mdlist = $mdcParser->parse($clean_mdc) or
@@ -342,7 +318,126 @@
     }
 
+    delete_tmpdir();
+
     carp($msg);
     exit $exit_code;
 }
 
+sub delete_tmpdir
+{
+    if (!$save_temps) {
+        system "rm -r $tmpdir";
+    }
+}
+
+# list of output data products for runs that were made before the configuration re-work 
+sub get_legacy_file_mdc
+{
+    my $list =
+"
+#
+# interesting things we might want to save in this file
+#   1. whether file is to be distributed in 'clean' mode
+#   2. whether file is to be cleaned (this should be the same thing)
+#   3. type of file 'no, not our business, precious'
+
+# this data should probably be computed by the relevant program and saved as part
+# of the configuration dump that is output.
+
+
+PROD_LIST MULTI
+
+PROD_LIST   METADATA
+        STAGE                    STR     RAW
+    # there is isn't really a file rule for raw images
+	DUMMY           	BOOL	F
+END
+
+# list of data products for a gpc1 chipProcessedImfile  (made by ppImage)
+PROD_LIST   METADATA
+        STAGE                    STR     CHIP
+	PPIMAGE.CONFIG  	BOOL	T
+#	PPIMAGE.CHIP    	BOOL	F
+#	PPIMAGE.CHIP.MASK	BOOL	F
+#	PPIMAGE.CHIP.VARIANCE	BOOL	F
+	PPIMAGE.BIN1    	BOOL	T
+	PPIMAGE.BIN2    	BOOL	T
+	PSPHOT.OUT.CMF.SPL	BOOL	T
+	PSPHOT.BACKMDL  	BOOL	T
+	PPIMAGE.STATS   	BOOL	T
+	LOG.IMFILE      	BOOL	T
+	TRACE.IMFILE    	BOOL	T
+    # where do we put exposure level data such as LOG.EXP ?
+END
+# exposure level output products from camera processing made by psastro and ppImage (jpegs)
+PROD_LIST   METADATA
+        STAGE                    STR     CAM
+	PSASTRO.CONFIG  	BOOL	T
+	PSASTRO.OUTPUT  	BOOL	T
+	PSASTRO.STATS   	BOOL	T
+	PPIMAGE.JPEG1   	BOOL	T
+	PPIMAGE.JPEG2   	BOOL	T
+	LOG.EXP         	BOOL	T
+	TRACE.EXP       	BOOL	T
+END
+# chip lelel output products from camera processing made by psastro
+PROD_LIST   METADATA
+        STAGE                    STR CAM_CHIP
+#	PSASTRO.OUTPUT.MASK	BOOL	F
+END
+PROD_LIST   METADATA
+        STAGE                    STR     FAKE
+#	PPSIM.OUTPUT    	BOOL	F
+END
+# list of data products for a gpc1 warpSkyfile (pswarp)
+PROD_LIST   METADATA
+        STAGE                    STR     WARP
+	PSWARP.CONFIG   	BOOL	T
+#	PSWARP.OUTPUT   	BOOL	F
+#	PSWARP.OUTPUT.MASK	BOOL	F
+#	PSWARP.OUTPUT.VARIANCE	BOOL	F
+	PSWARP.OUTPUT.SOURCES	BOOL	T
+	PSPHOT.BACKMDL.MEF  	BOOL	T
+	PSPHOT.PSF.SKY.SAVE	BOOL	T
+	SKYCELL.STATS   	BOOL	T
+	SKYCELL.TEMPLATE	BOOL	T
+	LOG.EXP         	BOOL	T
+	TRACE.EXP       	BOOL	T
+END
+# outputs from diffRun (ppSub)
+PROD_LIST   METADATA
+        STAGE                    STR     DIFF
+	PPSUB.CONFIG    	BOOL	T
+#	PPSUB.OUTPUT    	BOOL	F
+#	PPSUB.OUTPUT.MASK	BOOL	F
+#	PPSUB.OUTPUT.VARIANCE	BOOL	F
+	PPSUB.OUTPUT.KERNELS	BOOL	T
+	PPSUB.OUTPUT.JPEG1	BOOL	T
+	PPSUB.OUTPUT.JPEG2	BOOL	T
+	PSPHOT.OUT.CMF.MEF	BOOL	T
+	PSPHOT.BACKMDL.MEF	BOOL	T
+	SKYCELL.STATS   	BOOL	T
+	LOG.EXP         	BOOL	T
+	TRACE.EXP       	BOOL	T
+END
+PROD_LIST   METADATA
+        STAGE                    STR     STACK
+	PPSTACK.CONFIG  	BOOL	T
+#	PPSTACK.OUTPUT  	BOOL	F
+#	PPSTACK.OUTPUT.MASK	BOOL	F
+#	PPSTACK.OUTPUT.VARIANCE	BOOL	F
+	PPSTACK.TARGET.PSF	BOOL	T
+	PSPHOT.OUT.CMF.MEF	BOOL	T
+	PSPHOT.BACKMDL.MEF	BOOL	T
+	SKYCELL.STATS   	BOOL	T
+	PPSTACK.OUTPUT.JPEG1	BOOL	T
+	PPSTACK.OUTPUT.JPEG2	BOOL	T
+	LOG.EXP         	BOOL	T
+	TRACE.EXP       	BOOL	T
+END
+";
+    return $list;
+}
+
+
 __END__
Index: /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/register_imfile.pl
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/register_imfile.pl	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/register_imfile.pl	(revision 23594)
@@ -112,9 +112,9 @@
 
 # we require at a minimum: -telescope, -inst, -filelevel, -class_id, -exp_type
-if (uc(&value_for_flag ($cmdflags, "-telescope")) eq "NULL") { &my_die ("telescope not found", $exp_id, $tmp_exp_name, $tmp_class_id, $uri, $PS_EXIT_CONFIG_ERROR); }
-if (uc(&value_for_flag ($cmdflags, "-inst"))      eq "NULL") { &my_die ("inst      not found", $exp_id, $tmp_exp_name, $tmp_class_id, $uri, $PS_EXIT_CONFIG_ERROR); }
-if (uc(&value_for_flag ($cmdflags, "-filelevel")) eq "NULL") { &my_die ("filelevel not found", $exp_id, $tmp_exp_name, $tmp_class_id, $uri, $PS_EXIT_CONFIG_ERROR); }
-if (uc(&value_for_flag ($cmdflags, "-class_id"))  eq "NULL") { &my_die ("class_id  not found", $exp_id, $tmp_exp_name, $tmp_class_id, $uri, $PS_EXIT_CONFIG_ERROR); }
-if (uc(&value_for_flag ($cmdflags, "-exp_type"))  eq "NULL") { &my_die ("exp_type  not found", $exp_id, $tmp_exp_name, $tmp_class_id, $uri, $PS_EXIT_CONFIG_ERROR); }
+if (uc(&value_for_flag ($cmdflags, "NULL", "-telescope")) eq "NULL") { &my_die ("telescope not found", $exp_id, $tmp_exp_name, $tmp_class_id, $uri, $PS_EXIT_CONFIG_ERROR); }
+if (uc(&value_for_flag ($cmdflags, "NULL", "-inst"))      eq "NULL") { &my_die ("inst      not found", $exp_id, $tmp_exp_name, $tmp_class_id, $uri, $PS_EXIT_CONFIG_ERROR); }
+if (uc(&value_for_flag ($cmdflags, "NULL", "-filelevel")) eq "NULL") { &my_die ("filelevel not found", $exp_id, $tmp_exp_name, $tmp_class_id, $uri, $PS_EXIT_CONFIG_ERROR); }
+if (uc(&value_for_flag ($cmdflags, "NULL", "-class_id"))  eq "NULL") { &my_die ("class_id  not found", $exp_id, $tmp_exp_name, $tmp_class_id, $uri, $PS_EXIT_CONFIG_ERROR); }
+if (uc(&value_for_flag ($cmdflags, "NULL", "-exp_type"))  eq "NULL") { &my_die ("exp_type  not found", $exp_id, $tmp_exp_name, $tmp_class_id, $uri, $PS_EXIT_CONFIG_ERROR); }
 
 my $command = "$regtool -addprocessedimfile";
@@ -128,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, 0.0, "-longitude");
+my $latitude  = &value_for_flag($cmdflags, 0.0, "-latitude");
+my $elevation = &value_for_flag($cmdflags, 0.0, "-elevation");
+my $ra        = &value_for_flag($cmdflags, 0.0, "-ra");
+my $dec       = &value_for_flag($cmdflags, 0.0, "-decl");
+my $dateobs   = &value_for_flag($cmdflags, 0.0, "-dateobs");
 
 # if the needed data is available, pass it to sunmoon:
@@ -193,7 +193,8 @@
 {
     my $cmdflags = shift;
+    my $default = shift;
     my $flag = shift;
 
-    my $value = 0.0;
+    my $value = $default;
     if ($cmdflags =~ m|$flag|) {
         ($value) = $cmdflags =~ m|$flag\s+(\S+)|;
Index: /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/stack_skycell.pl
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/stack_skycell.pl	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/stack_skycell.pl	(revision 23594)
@@ -203,5 +203,5 @@
 # Perform stacking
 unless ($no_op) {
-    my $command = "$ppStack $listName $outroot";
+    my $command = "$ppStack -input $listName $outroot";
     $command .= " -stats $outputStats" if $do_stats;;
     $command .= " -recipe PPSTACK $recipe_ppStack";
Index: /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/summit_copy.pl
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/summit_copy.pl	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/summit_copy.pl	(revision 23594)
@@ -46,5 +46,5 @@
 
 pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
-pod2usage( -msg => "Required options: --uri --filename --exp_name --inst --telescope --class --class_id --workdir",
+pod2usage( -msg => "Required options: --uri --filename --exp_name --inst --telescope --class --class_id",
        -exitval => 3)
     unless defined $uri
@@ -54,6 +54,5 @@
     and defined $telescope
     and defined $class
-    and defined $class_id
-    and defined $workdir;
+    and defined $class_id;
 
 # Look for programs we need
@@ -107,7 +106,7 @@
 $command .= " -class_id $class_id";
 $command .= " -uri $filename";
-$command .= " -workdir $workdir";
+# $command .= " -workdir $workdir";
 $command .= " -hostname $host";
-$command .= " -end_stage $end_stage" if defined $end_stage;
+# $command .= " -end_stage $end_stage" if defined $end_stage;
 $command .= " -dbname $dbname" if defined $dbname;
 
Index: /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/warp_skycell.pl
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/warp_skycell.pl	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippScripts/scripts/warp_skycell.pl	(revision 23594)
@@ -36,5 +36,5 @@
 }
 
-my ($warp_id, $skycell_id, $warp_skyfile_id, $tess_dir, $camera, $dbname, $outroot, $threads, $run_state, $verbose, $no_update, $no_op, $redirect, $save_temps);
+my ($warp_id, $skycell_id, $warp_skyfile_id, $tess_dir, $camera, $dbname, $outroot, $threads, $run_state, $magicked, $verbose, $no_update, $no_op, $redirect, $save_temps);
 GetOptions(
     'warp_id|i=s'         => \$warp_id, # Warp identifier
@@ -47,4 +47,5 @@
     'threads=s'           => \$threads,   # Number of threads to use for pswarp
     'run-state=s'         => \$run_state,  # 'new' or 'update'
+    'magicked'            => \$magicked,  # input run has been magicked already?
     'verbose'             => \$verbose,   # Print to stdout
     'no-update'           => \$no_update, # Don't update the database?
@@ -246,4 +247,5 @@
             $command .= " -tess_id $tess_dir";
             $command .= " -path_base $outroot"; # needed for logfile lookups
+            $command .= " -magicked" if $magicked;
 
             $command .= " -uri $outputImage" if $accept;
Index: /branches/cnb_branches/cnb_branch_20090301/ippTasks/Makefile.am
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTasks/Makefile.am	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTasks/Makefile.am	(revision 23594)
@@ -1,4 +1,3 @@
 task_files = \
-	detrend.mkruns.pro \
 	detrend.norm.pro \
 	detrend.process.pro \
@@ -7,4 +6,5 @@
 	detrend.stack.pro \
 	detrend.correct.pro \
+	detrend.cleanup.pro \
 	flatcorr.pro \
 	automate.pro \
@@ -36,4 +36,6 @@
 	simtest.flatcorr.config \
 	simtest.flatcorr.auto \
+	simtest.ctemask.config \
+	simtest.ctemask.auto \
 	simtest.stack.config \
 	simtest.stack.auto
Index: /branches/cnb_branches/cnb_branch_20090301/ippTasks/chip.pro
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTasks/chip.pro	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTasks/chip.pro	(revision 23594)
@@ -142,4 +142,6 @@
     book getword chipPendingImfile $pageName exp_id -var EXP_ID
     book getword chipPendingImfile $pageName exp_tag -var EXP_TAG
+    book getword chipPendingImfile $pageName raw_magicked -var RAW_MAGICKED
+    book getword chipPendingImfile $pageName deburned -var DEBURNED
     book getword chipPendingImfile $pageName chip_id -var CHIP_ID
     book getword chipPendingImfile $pageName chip_imfile_id -var CHIP_IMFILE_ID
@@ -151,4 +153,10 @@
     book getword chipPendingImfile $pageName state -var RUN_STATE
 
+    if ("$RAW_MAGICKED" == "T")
+        $MAGICKED_ARG = "--magicked"
+    else 
+        $MAGICKED_ARG = ""
+    end
+
     # specify choice of local or remote host based on camera and chip (class_id)
     set.host.for.camera $CAMERA $CLASS_ID
@@ -171,5 +179,5 @@
     stderr $LOGDIR/chip.imfile.log
 
-    $run = chip_imfile.pl --threads @MAX_THREADS@ --exp_id $EXP_ID --chip_id $CHIP_ID --chip_imfile_id $CHIP_IMFILE_ID --class_id $CLASS_ID --uri $URI --camera $CAMERA --run-state $RUN_STATE --outroot $outroot --redirect-output 
+    $run = chip_imfile.pl --threads @MAX_THREADS@ --exp_id $EXP_ID --chip_id $CHIP_ID --chip_imfile_id $CHIP_IMFILE_ID --class_id $CLASS_ID --uri $URI --camera $CAMERA --run-state $RUN_STATE $MAGICKED_ARG --deburned $DEBURNED --outroot $outroot --redirect-output
     if ("$REDUCTION" != "NULL")
       $run = $run --reduction $REDUCTION
Index: /branches/cnb_branches/cnb_branch_20090301/ippTasks/detrend.cleanup.pro
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTasks/detrend.cleanup.pro	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTasks/detrend.cleanup.pro	(revision 23594)
@@ -0,0 +1,1098 @@
+## detrend.process.pro : globals and support macros : -*- sh -*-
+## this file contains the tasks for running the detrend processing stage
+## these tasks use the books detPendingProcessedImfile and detPendingProcessedExp
+
+# test for required global variables
+check.globals
+
+book init detCleanupProcessedImfile
+book init detCleanupProcessedExp
+book init detCleanupStackedImfile
+book init detCleanupNormStatImfile
+book init detCleanupNormImfile
+book init detCleanupNormExp
+book init detCleanupResidImfile
+book init detCleanupResidExp
+
+macro detclean.reset
+  book init detCleanupProcessedImfile
+  book init detCleanupProcessedExp
+  book init detCleanupStackedImfile
+
+  book init detCleanupNormStatImfile
+  book init detCleanupNormImfile
+  book init detCleanupNormExp
+
+  book init detCleanupResidImfile
+  book init detCleanupResidExp
+end
+
+macro detclean.status
+  echo detCleanupProcessedImfile
+  book listbook detCleanupProcessedImfile
+  echo detCleanupProcessedExp
+  book listbook detCleanupProcessedExp
+  echo detCleanupStackedImfile
+  book listbook detCleanupStackedImfile
+
+  book listbook detCleanupNormStatImfile
+  book listbook detCleanupNormImfile
+  book listbook detCleanupNormExp
+  book listbook detCleanupResidImfile
+  book listbook detCleanupResidExp
+end
+
+macro detclean.on
+  task detrend.cleanup.process.load
+    active true
+  end
+  task detrend.cleanup.process.run
+    active true
+  end
+  task detrend.cleanup.processexp.load
+    active true
+  end
+  task detrend.cleanup.processexp.run
+    active true
+  end
+  task detrend.cleanup.stack.load
+    active true
+  end
+  task detrend.cleanup.stack.run
+    active true
+  end
+
+  task detrend.cleanup.norm.load
+    active true
+  end
+  task detrend.cleanup.norm.run
+    active true
+  end
+  task detrend.cleanup.normexp.load
+    active true
+  end
+  task detrend.cleanup.normexp.run
+    active true
+  end
+  task detrend.cleanup.normstat.load
+    active true
+  end
+  task detrend.cleanup.normstat.run
+    active true
+  end
+
+  task detrend.cleanup.resid.load
+    active true
+  end
+  task detrend.cleanup.resid.run
+    active true
+  end
+  task detrend.cleanup.residexp.load
+    active true
+  end
+  task detrend.cleanup.residexp.run
+    active true
+  end
+end
+
+macro detclean.off
+  task detrend.cleanup.process.load
+    active false
+  end
+  task detrend.cleanup.process.run
+    active false
+  end
+  task detrend.cleanup.processexp.load
+    active false
+  end
+  task detrend.cleanup.processexp.run
+    active false
+  end
+  task detrend.cleanup.stack.load
+    active false
+  end
+  task detrend.cleanup.stack.run
+    active false
+  end
+  task detrend.cleanup.norm.load
+    active false
+  end
+  task detrend.cleanup.norm.run
+    active false
+  end
+  task detrend.cleanup.normexp.load
+    active false
+  end
+  task detrend.cleanup.normexp.run
+    active false
+  end
+  task detrend.cleanup.normstat.load
+    active false
+  end
+  task detrend.cleanup.normstat.run
+    active false
+  end
+  task detrend.cleanup.resid.load
+    active false
+  end
+  task detrend.cleanup.resid.run
+    active false
+  end
+  task detrend.cleanup.residexp.load
+    active false
+  end
+  task detrend.cleanup.residexp.run
+    active false
+  end
+end
+
+
+# these variables will cycle through the known database names
+$detCleanupProcessedImfile_DB = 0
+$detCleanupProcessedExp_DB = 0
+$detCleanupStackedImfile_DB = 0
+$detCleanupNormStatImfile_DB = 0
+$detCleanupNormImfile_DB = 0
+$detCleanupNormExp_DB = 0
+$detCleanupResidImfile_DB = 0
+$detCleanupResidExp_DB = 0
+
+######## cleanup process imfile ########
+task	       detrend.cleanup.process.load
+  host         local
+
+  periods      -poll $LOADPOLL
+  periods      -exec $LOADEXEC
+  periods      -timeout 30
+  npending     1
+  active       true
+
+  stdout NULL
+  stderr $LOGDIR/detrend.cleanup.process.imfile.log
+
+  task.exec
+    $run = dettool -pendingcleanup_processedimfile
+    if ($DB:n == 0)
+      option DEFAULT
+    else
+      # save the DB name for the exit tasks
+      option $DB:$detCleanupProcessedImfile_DB
+      $run = $run -dbname $DB:$detCleanupProcessedImfile_DB
+      $detCleanupProcessedImfile_DB ++
+      if ($detCleanupProcessedImfile_DB >= $DB:n) set detCleanupProcessedImfile_DB = 0
+    end
+    add_poll_args run
+    command $run
+  end
+
+  # success
+  task.exit    0
+    # convert 'stdout' to book format
+    ipptool2book stdout detCleanupProcessedImfile -key det_id:exp_id:class_id -uniq -setword dbname $options:0 -setword pantaskState INIT
+    if ($VERBOSE > 2)
+      book listbook detCleanupProcessedImfile
+    end
+
+    # delete existing entries in the appropriate pantaskStates
+    process_cleanup detCleanupProcessedImfile
+  end
+
+  # locked list
+  task.exit    default
+    showcommand failure
+  end
+
+  task.exit    crash
+    showcommand crash
+  end
+
+  # operation times out?
+  task.exit    timeout
+    showcommand timeout
+  end
+end
+
+# run the ipp_cleanup.pl script on pending images
+task	       detrend.cleanup.process.run
+  periods      -poll $RUNPOLL
+  periods      -exec $RUNEXEC
+  periods      -timeout 60
+  active       true
+
+  task.exec
+    book npages detCleanupProcessedImfile -var N
+    if ($N == 0) break
+    if ($NETWORK == 0) break
+    
+    # look for new images in detCleanupProcessedImfile (pantaskState == INIT)
+    book getpage detCleanupProcessedImfile 0 -var pageName -key pantaskState INIT
+    if ("$pageName" == "NULL") break
+
+    book setword detCleanupProcessedImfile $pageName pantaskState RUN
+    book getword detCleanupProcessedImfile $pageName det_id   -var DET_ID   
+    book getword detCleanupProcessedImfile $pageName exp_id   -var EXP_ID   
+    book getword detCleanupProcessedImfile $pageName class_id -var CLASS_ID 
+    book getword detCleanupProcessedImfile $pageName camera   -var CAMERA
+    book getword detCleanupProcessedImfile $pageName state    -var CLEANUP_MODE
+    book getword detCleanupProcessedImfile $pageName dbname   -var DBNAME
+
+    # specify choice of local or remote host based on camera and chip (class_id)
+    set.host.for.camera $CAMERA FPA
+
+    stdout $LOGDIR/detrend.cleanup.process.imfile.log
+    stderr $LOGDIR/detrend.cleanup.process.imfile.log
+
+    # XXX is everything listed here needed?
+    $run = ipp_cleanup.pl --stage detrend.process.imfile --stage_id $DET_ID --exp_id $EXP_ID --class_id $CLASS_ID --camera $CAMERA --mode $CLEANUP_MODE
+    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 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
+
+  # operation timed out?
+  task.exit    timeout
+    showcommand timeout
+    book setword detCleanupProcessedImfile $options:0 pantaskState TIMEOUT
+  end
+end
+ 
+
+######## cleanup process exp ########
+task	       detrend.cleanup.processexp.load
+  host         local
+
+  periods      -poll $LOADPOLL
+  periods      -exec $LOADEXEC
+  periods      -timeout 30
+  npending     1
+  active       true
+
+  stdout NULL
+  stderr $LOGDIR/detrend.cleanup.process.exp.log
+
+  task.exec
+    $run = dettool -pendingcleanup_processedexp
+    if ($DB:n == 0)
+      option DEFAULT
+    else
+      # save the DB name for the exit tasks
+      option $DB:$detCleanupProcessedExp_DB
+      $run = $run -dbname $DB:$detCleanupProcessedExp_DB
+      $detCleanupProcessedExp_DB ++
+      if ($detCleanupProcessedExp_DB >= $DB:n) set detCleanupProcessedExp_DB = 0
+    end
+    add_poll_args run
+    command $run
+  end
+
+  # success
+  task.exit    0
+    # convert 'stdout' to book format
+    ipptool2book stdout detCleanupProcessedExp -key det_id:exp_id -uniq -setword dbname $options:0 -setword pantaskState INIT
+    if ($VERBOSE > 2)
+      book listbook detCleanupProcessedExp
+    end
+
+    # delete existing entries in the appropriate pantaskStates
+    process_cleanup detCleanupProcessedExp
+  end
+
+  # locked list
+  task.exit    default
+    showcommand failure
+  end
+
+  task.exit    crash
+    showcommand crash
+  end
+
+  # operation times out?
+  task.exit    timeout
+    showcommand timeout
+  end
+end
+
+# run the ipp_cleanup.pl script on pending images
+task	       detrend.cleanup.processexp.run
+  periods      -poll $RUNPOLL
+  periods      -exec $RUNEXEC
+  periods      -timeout 60
+  active       true
+
+  task.exec
+    book npages detCleanupProcessedExp -var N
+    if ($N == 0) break
+    if ($NETWORK == 0) break
+    
+    # look for new images in detCleanupProcessedExp (pantaskState == INIT)
+    book getpage detCleanupProcessedExp 0 -var pageName -key pantaskState INIT
+    if ("$pageName" == "NULL") break
+
+    book setword detCleanupProcessedExp $pageName pantaskState RUN
+    book getword detCleanupProcessedExp $pageName det_id   -var DET_ID   
+    book getword detCleanupProcessedExp $pageName exp_id   -var EXP_ID   
+    book getword detCleanupProcessedExp $pageName camera -var CAMERA
+    book getword detCleanupProcessedExp $pageName state -var CLEANUP_MODE
+    book getword detCleanupProcessedExp $pageName dbname -var DBNAME
+
+    # specify choice of local or remote host based on camera and chip (class_id)
+    set.host.for.camera $CAMERA FPA
+
+    stdout $LOGDIR/detrend.cleanup.process.exp.log
+    stderr $LOGDIR/detrend.cleanup.process.exp.log
+
+    # XXX is everything listed here needed?
+    $run = ipp_cleanup.pl --stage detrend.process.exp --stage_id $DET_ID --exp_id $EXP_ID --camera $CAMERA --mode $CLEANUP_MODE
+    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 detCleanupProcessedExp $options:0 $JOB_STATUS
+  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
+    showcommand timeout
+    book setword detCleanupProcessedExp $options:0 pantaskState TIMEOUT
+  end
+end
+
+########## cleanup stack ###########
+task	       detrend.cleanup.stack.load
+  host         local
+
+  periods      -poll $LOADPOLL
+  periods      -exec $LOADEXEC
+  periods      -timeout 30
+  npending     1
+  active       true
+
+  stdout NULL
+  stderr $LOGDIR/detrend.cleanup.stack.log
+
+  task.exec
+    $run = dettool -pendingcleanup_stacked
+    if ($DB:n == 0)
+      option DEFAULT
+    else
+      # save the DB name for the exit tasks
+      option $DB:$detCleanupStackedImfile_DB
+      $run = $run -dbname $DB:$detCleanupStackedImfile_DB
+      $detCleanupStackedImfile_DB ++
+      if ($detCleanupStackedImfile_DB >= $DB:n) set detCleanupStackedImfile_DB = 0
+    end
+    add_poll_args run
+    command $run
+  end
+
+  # success
+  task.exit    0
+    # convert 'stdout' to book format
+    ipptool2book stdout detCleanupStackedImfile -key det_id:iteration:class_id -uniq -setword dbname $options:0 -setword pantaskState INIT
+    if ($VERBOSE > 2)
+      book listbook detCleanupStackedImfile
+    end
+
+    # delete existing entries in the appropriate pantaskStates
+    process_cleanup detCleanupStackedImfile
+  end
+
+  # locked list
+  task.exit    default
+    showcommand failure
+  end
+
+  task.exit    crash
+    showcommand crash
+  end
+
+  # operation times out?
+  task.exit    timeout
+    showcommand timeout
+  end
+end
+
+# run the ipp_cleanup.pl script on pending images
+task	       detrend.cleanup.stack.run
+  periods      -poll $RUNPOLL
+  periods      -exec $RUNEXEC
+  periods      -timeout 60
+  active       true
+
+  task.exec
+    book npages detCleanupStackedImfile -var N
+    if ($N == 0) break
+    if ($NETWORK == 0) break
+    
+    # look for new images in detCleanupStackedImfile (pantaskState == INIT)
+    book getpage detCleanupStackedImfile 0 -var pageName -key pantaskState INIT
+    if ("$pageName" == "NULL") break
+
+    book setword detCleanupStackedImfile $pageName pantaskState RUN
+    book getword detCleanupStackedImfile $pageName det_id   -var DET_ID   
+    book getword detCleanupStackedImfile $pageName iteration -var ITERATION
+    book getword detCleanupStackedImfile $pageName class_id -var CLASS_ID 
+    book getword detCleanupStackedImfile $pageName camera -var CAMERA
+    book getword detCleanupStackedImfile $pageName state -var CLEANUP_MODE
+    book getword detCleanupStackedImfile $pageName dbname -var DBNAME
+
+    # specify choice of local or remote host based on camera and chip (class_id)
+    set.host.for.camera $CAMERA FPA
+
+    stdout $LOGDIR/detrend.cleanup.stack.log
+    stderr $LOGDIR/detrend.cleanup.stack.log
+
+    # XXX is everything listed here needed?
+    $run = ipp_cleanup.pl --stage detrend.stack.imfile --stage_id $DET_ID --iteration $ITERATION --class_id $CLASS_ID --camera $CAMERA --mode $CLEANUP_MODE
+    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 detCleanupStackedImfile $options:0 $JOB_STATUS
+  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
+    showcommand timeout
+    book setword detCleanupStackedImfile $options:0 pantaskState TIMEOUT
+  end
+end
+ 
+
+########## cleanup normstat ###########
+task	       detrend.cleanup.normstat.load
+  host         local
+
+  periods      -poll $LOADPOLL
+  periods      -exec $LOADEXEC
+  periods      -timeout 30
+  npending     1
+  active       true
+
+  stdout NULL
+  stderr $LOGDIR/detrend.cleanup.normstat.log
+
+  task.exec
+    $run = dettool -pendingcleanup_normalizedstat
+    if ($DB:n == 0)
+      option DEFAULT
+    else
+      # save the DB name for the exit tasks
+      option $DB:$detCleanupNormStatImfile_DB
+      $run = $run -dbname $DB:$detCleanupNormStatImfile_DB
+      $detCleanupNormStatImfile_DB ++
+      if ($detCleanupNormStatImfile_DB >= $DB:n) set detCleanupNormStatImfile_DB = 0
+    end
+    add_poll_args run
+    command $run
+  end
+
+  # success
+  task.exit    0
+    # convert 'stdout' to book format
+    ipptool2book stdout detCleanupNormStatImfile -key det_id:iteration -uniq -setword dbname $options:0 -setword pantaskState INIT
+    if ($VERBOSE > 2)
+      book listbook detCleanupNormStatImfile
+    end
+
+    # delete existing entries in the appropriate pantaskStates
+    process_cleanup detCleanupNormStatImfile
+  end
+
+  # locked list
+  task.exit    default
+    showcommand failure
+  end
+
+  task.exit    crash
+    showcommand crash
+  end
+
+  # operation times out?
+  task.exit    timeout
+    showcommand timeout
+  end
+end
+
+# run the ipp_cleanup.pl script on pending images
+task	       detrend.cleanup.normstat.run
+  periods      -poll $RUNPOLL
+  periods      -exec $RUNEXEC
+  periods      -timeout 60
+  active       true
+
+  task.exec
+    book npages detCleanupNormStatImfile -var N
+    if ($N == 0) break
+    if ($NETWORK == 0) break
+    
+    # look for new images in detCleanupNormStatImfile (pantaskState == INIT)
+    book getpage detCleanupNormStatImfile 0 -var pageName -key pantaskState INIT
+    if ("$pageName" == "NULL") break
+
+    book setword detCleanupNormStatImfile $pageName pantaskState RUN
+    book getword detCleanupNormStatImfile $pageName det_id   -var DET_ID   
+    book getword detCleanupNormStatImfile $pageName iteration -var ITERATION
+    book getword detCleanupNormStatImfile $pageName camera -var CAMERA
+    book getword detCleanupNormStatImfile $pageName state -var CLEANUP_MODE
+    book getword detCleanupNormStatImfile $pageName dbname -var DBNAME
+
+    # specify choice of local or remote host based on camera and chip (class_id)
+    set.host.for.camera $CAMERA FPA
+
+    stdout $LOGDIR/detrend.cleanup.normstat.log
+    stderr $LOGDIR/detrend.cleanup.normstat.log
+
+    # XXX is everything listed here needed?
+    $run = ipp_cleanup.pl --stage detrend.normstat.imfile --stage_id $DET_ID --iteration $ITERATION --camera $CAMERA --mode $CLEANUP_MODE
+    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 detCleanupNormStatImfile $options:0 $JOB_STATUS
+  end
+
+  task.exit    crash
+    showcommand crash
+    book setword detCleanupNormStatImfile $options:0 pantaskState CRASH
+  end
+
+  # operation timed out?
+  task.exit    timeout
+    showcommand timeout
+    book setword detCleanupNormStatImfile $options:0 pantaskState TIMEOUT
+  end
+end
+ 
+########## cleanup norm (normalized.imfile) ###########
+task	       detrend.cleanup.norm.load
+  host         local
+
+  periods      -poll $LOADPOLL
+  periods      -exec $LOADEXEC
+  periods      -timeout 30
+  npending     1
+  active       true
+
+  stdout NULL
+  stderr $LOGDIR/detrend.cleanup.norm.log
+
+  task.exec
+    $run = dettool -pendingcleanup_normalizedimfile
+    if ($DB:n == 0)
+      option DEFAULT
+    else
+      # save the DB name for the exit tasks
+      option $DB:$detCleanupNormImfile_DB
+      $run = $run -dbname $DB:$detCleanupNormImfile_DB
+      $detCleanupNormImfile_DB ++
+      if ($detCleanupNormImfile_DB >= $DB:n) set detCleanupNormImfile_DB = 0
+    end
+    add_poll_args run
+    command $run
+  end
+
+  # success
+  task.exit    0
+    # convert 'stdout' to book format
+    ipptool2book stdout detCleanupNormImfile -key det_id:iteration:class_id -uniq -setword dbname $options:0 -setword pantaskState INIT
+    if ($VERBOSE > 2)
+      book listbook detCleanupNormImfile
+    end
+
+    # delete existing entries in the appropriate pantaskStates
+    process_cleanup detCleanupNormImfile
+  end
+
+  # locked list
+  task.exit    default
+    showcommand failure
+  end
+
+  task.exit    crash
+    showcommand crash
+  end
+
+  # operation times out?
+  task.exit    timeout
+    showcommand timeout
+  end
+end
+
+# run the ipp_cleanup.pl script on pending images
+task	       detrend.cleanup.norm.run
+  periods      -poll $RUNPOLL
+  periods      -exec $RUNEXEC
+  periods      -timeout 60
+  active       true
+
+  task.exec
+    book npages detCleanupNormImfile -var N
+    if ($N == 0) break
+    if ($NETWORK == 0) break
+    
+    # look for new images in detCleanupNormImfile (pantaskState == INIT)
+    book getpage detCleanupNormImfile 0 -var pageName -key pantaskState INIT
+    if ("$pageName" == "NULL") break
+
+    book setword detCleanupNormImfile $pageName pantaskState RUN
+    book getword detCleanupNormImfile $pageName det_id   -var DET_ID   
+    book getword detCleanupNormImfile $pageName iteration -var ITERATION
+    book getword detCleanupNormImfile $pageName class_id -var CLASS_ID 
+    book getword detCleanupNormImfile $pageName camera -var CAMERA
+    book getword detCleanupNormImfile $pageName state -var CLEANUP_MODE
+    book getword detCleanupNormImfile $pageName dbname -var DBNAME
+
+    # specify choice of local or remote host based on camera and chip (class_id)
+    set.host.for.camera $CAMERA FPA
+
+    stdout $LOGDIR/detrend.cleanup.norm.log
+    stderr $LOGDIR/detrend.cleanup.norm.log
+
+    # XXX is everything listed here needed?
+    $run = ipp_cleanup.pl --stage detrend.norm.imfile --stage_id $DET_ID --iteration $ITERATION --class_id $CLASS_ID --camera $CAMERA --mode $CLEANUP_MODE
+    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 detCleanupNormImfile $options:0 $JOB_STATUS
+  end
+
+  task.exit    crash
+    showcommand crash
+    book setword detCleanupNormImfile $options:0 pantaskState CRASH
+  end
+
+  # operation timed out?
+  task.exit    timeout
+    showcommand timeout
+    book setword detCleanupNormImfile $options:0 pantaskState TIMEOUT
+  end
+end
+ 
+########## cleanup normexp ###########
+task	       detrend.cleanup.normexp.load
+  host         local
+
+  periods      -poll $LOADPOLL
+  periods      -exec $LOADEXEC
+  periods      -timeout 30
+  npending     1
+  active       true
+
+  stdout NULL
+  stderr $LOGDIR/detrend.normexp.cleanup.log
+
+  task.exec
+    $run = dettool -pendingcleanup_normalizedexp
+    if ($DB:n == 0)
+      option DEFAULT
+    else
+      # save the DB name for the exit tasks
+      option $DB:$detCleanupNormExp_DB
+      $run = $run -dbname $DB:$detCleanupNormExp_DB
+      $detCleanupNormExp_DB ++
+      if ($detCleanupNormExp_DB >= $DB:n) set detCleanupNormExp_DB = 0
+    end
+    add_poll_args run
+    command $run
+  end
+
+  # success
+  task.exit    0
+    # convert 'stdout' to book format
+    ipptool2book stdout detCleanupNormExp -key det_id:iteration -uniq -setword dbname $options:0 -setword pantaskState INIT
+    if ($VERBOSE > 2)
+      book listbook detCleanupNormExp
+    end
+
+    # delete existing entries in the appropriate pantaskStates
+    process_cleanup detCleanupNormExp
+  end
+
+  # locked list
+  task.exit    default
+    showcommand failure
+  end
+
+  task.exit    crash
+    showcommand crash
+  end
+
+  # operation times out?
+  task.exit    timeout
+    showcommand timeout
+  end
+end
+
+# run the ipp_cleanup.pl script on pending images
+task	       detrend.cleanup.normexp.run
+  periods      -poll $RUNPOLL
+  periods      -exec $RUNEXEC
+  periods      -timeout 60
+  active       true
+
+  task.exec
+    book npages detCleanupNormExp -var N
+    if ($N == 0) break
+    if ($NETWORK == 0) break
+    
+    # look for new images in detCleanupNormExp (pantaskState == INIT)
+    book getpage detCleanupNormExp 0 -var pageName -key pantaskState INIT
+    if ("$pageName" == "NULL") break
+
+    book setword detCleanupNormExp $pageName pantaskState RUN
+    book getword detCleanupNormExp $pageName det_id   -var DET_ID   
+    book getword detCleanupNormExp $pageName iteration -var ITERATION
+    book getword detCleanupNormExp $pageName camera -var CAMERA
+    book getword detCleanupNormExp $pageName state -var CLEANUP_MODE
+    book getword detCleanupNormExp $pageName dbname -var DBNAME
+
+    # specify choice of local or remote host based on camera and chip (class_id)
+    set.host.for.camera $CAMERA FPA
+
+    stdout $LOGDIR/detrend.cleanup.normexp.log
+    stderr $LOGDIR/detrend.cleanup.normexp.log
+
+    # XXX is everything listed here needed?
+    $run = ipp_cleanup.pl --stage detrend.norm.exp --stage_id $DET_ID --iteration $ITERATION --camera $CAMERA --mode $CLEANUP_MODE
+    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 detCleanupNormExp $options:0 $JOB_STATUS
+  end
+
+  task.exit    crash
+    showcommand crash
+    book setword detCleanupNormExp $options:0 pantaskState CRASH
+  end
+
+  # operation timed out?
+  task.exit    timeout
+    showcommand timeout
+    book setword detCleanupNormExp $options:0 pantaskState TIMEOUT
+  end
+end
+ 
+
+######## cleanup resid imfile ########
+task	       detrend.cleanup.resid.load
+  host         local
+
+  periods      -poll $LOADPOLL
+  periods      -exec $LOADEXEC
+  periods      -timeout 30
+  npending     1
+  active       true
+
+  stdout NULL
+  stderr $LOGDIR/detrend.cleanup.resid.imfile.log
+
+  task.exec
+    $run = dettool -pendingcleanup_residimfile
+    if ($DB:n == 0)
+      option DEFAULT
+    else
+      # save the DB name for the exit tasks
+      option $DB:$detCleanupResidImfile_DB
+      $run = $run -dbname $DB:$detCleanupResidImfile_DB
+      $detCleanupResidImfile_DB ++
+      if ($detCleanupResidImfile_DB >= $DB:n) set detCleanupResidImfile_DB = 0
+    end
+    add_poll_args run
+    command $run
+  end
+
+  # success
+  task.exit    0
+    # convert 'stdout' to book format
+    ipptool2book stdout detCleanupResidImfile -key det_id:iteration:exp_id:class_id -uniq -setword dbname $options:0 -setword pantaskState INIT
+    if ($VERBOSE > 2)
+      book listbook detCleanupResidImfile
+    end
+
+    # delete existing entries in the appropriate pantaskStates
+    process_cleanup detCleanupResidImfile
+  end
+
+  # locked list
+  task.exit    default
+    showcommand failure
+  end
+
+  task.exit    crash
+    showcommand crash
+  end
+
+  # operation times out?
+  task.exit    timeout
+    showcommand timeout
+  end
+end
+
+# run the ipp_cleanup.pl script on pending images
+task	       detrend.cleanup.resid.run
+  periods      -poll $RUNPOLL
+  periods      -exec $RUNEXEC
+  periods      -timeout 60
+  active       true
+
+  task.exec
+    book npages detCleanupResidImfile -var N
+    if ($N == 0) break
+    if ($NETWORK == 0) break
+    
+    # look for new images in detCleanupResidImfile (pantaskState == INIT)
+    book getpage detCleanupResidImfile 0 -var pageName -key pantaskState INIT
+    if ("$pageName" == "NULL") break
+
+    book setword detCleanupResidImfile $pageName pantaskState RUN
+    book getword detCleanupResidImfile $pageName det_id   -var DET_ID   
+    book getword detCleanupResidImfile $pageName exp_id   -var EXP_ID   
+    book getword detCleanupResidImfile $pageName class_id -var CLASS_ID 
+    book getword detCleanupResidImfile $pageName iteration -var ITERATION     
+    book getword detCleanupResidImfile $pageName camera -var CAMERA
+    book getword detCleanupResidImfile $pageName state -var CLEANUP_MODE
+    book getword detCleanupResidImfile $pageName dbname -var DBNAME
+
+    # specify choice of local or remote host based on camera and chip (class_id)
+    set.host.for.camera $CAMERA FPA
+
+    stdout $LOGDIR/detrend.cleanup.resid.imfile.log
+    stderr $LOGDIR/detrend.cleanup.resid.imfile.log
+
+    # XXX is everything listed here needed?
+    $run = ipp_cleanup.pl --stage detrend.resid.imfile --stage_id $DET_ID --exp_id $EXP_ID --class_id $CLASS_ID --iteration $ITERATION --camera $CAMERA --mode $CLEANUP_MODE
+    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 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
+
+  # operation timed out?
+  task.exit    timeout
+    showcommand timeout
+    book setword detCleanupResidImfile $options:0 pantaskState TIMEOUT
+  end
+end
+ 
+
+######## cleanup resid exp ########
+task	       detrend.cleanup.residexp.load
+  host         local
+
+  periods      -poll $LOADPOLL
+  periods      -exec $LOADEXEC
+  periods      -timeout 30
+  npending     1
+  active       true
+
+  stdout NULL
+  stderr $LOGDIR/detrend.cleanup.resid.exp.log
+
+  task.exec
+    $run = dettool -pendingcleanup_residexp
+    if ($DB:n == 0)
+      option DEFAULT
+    else
+      # save the DB name for the exit tasks
+      option $DB:$detCleanupResidExp_DB
+      $run = $run -dbname $DB:$detCleanupResidExp_DB
+      $detCleanupResidExp_DB ++
+      if ($detCleanupResidExp_DB >= $DB:n) set detCleanupResidExp_DB = 0
+    end
+    add_poll_args run
+    command $run
+  end
+
+  # success
+  task.exit    0
+    # convert 'stdout' to book format
+    ipptool2book stdout detCleanupResidExp -key det_id:iteration:exp_id -uniq -setword dbname $options:0 -setword pantaskState INIT
+    if ($VERBOSE > 2)
+      book listbook detCleanupResidExp
+    end
+
+    # delete existing entries in the appropriate pantaskStates
+    process_cleanup detCleanupResidExp
+  end
+
+  # locked list
+  task.exit    default
+    showcommand failure
+  end
+
+  task.exit    crash
+    showcommand crash
+  end
+
+  # operation times out?
+  task.exit    timeout
+    showcommand timeout
+  end
+end
+
+# run the ipp_cleanup.pl script on pending images
+task	       detrend.cleanup.residexp.run
+  periods      -poll $RUNPOLL
+  periods      -exec $RUNEXEC
+  periods      -timeout 60
+  active       true
+
+  task.exec
+    book npages detCleanupResidExp -var N
+    if ($N == 0) break
+    if ($NETWORK == 0) break
+    
+    # look for new images in detCleanupResidExp (pantaskState == INIT)
+    book getpage detCleanupResidExp 0 -var pageName -key pantaskState INIT
+    if ("$pageName" == "NULL") break
+
+    book setword detCleanupResidExp $pageName pantaskState RUN
+    book getword detCleanupResidExp $pageName det_id    -var DET_ID   
+    book getword detCleanupResidExp $pageName exp_id    -var EXP_ID   
+    book getword detCleanupResidExp $pageName iteration -var ITERATION
+    book getword detCleanupResidExp $pageName camera 	-var CAMERA
+    book getword detCleanupResidExp $pageName state  	-var CLEANUP_MODE
+    book getword detCleanupResidExp $pageName dbname 	-var DBNAME
+
+    # specify choice of local or remote host based on camera and chip (class_id)
+    set.host.for.camera $CAMERA FPA
+
+    stdout $LOGDIR/detrend.cleanup.resid.exp.log
+    stderr $LOGDIR/detrend.cleanup.resid.exp.log
+
+    # XXX is everything listed here needed?
+    $run = ipp_cleanup.pl --stage detrend.resid.exp --stage_id $DET_ID --exp_id $EXP_ID --class_id $CLASS_ID --camera $CAMERA --mode $CLEANUP_MODE
+    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 detCleanupResidExp $options:0 $JOB_STATUS
+  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
+    showcommand timeout
+    book setword detCleanupResidExp $options:0 pantaskState TIMEOUT
+  end
+end
Index: anches/cnb_branches/cnb_branch_20090301/ippTasks/detrend.mkruns.pro
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTasks/detrend.mkruns.pro	(revision 23593)
+++ 	(revision )
@@ -1,114 +1,0 @@
-## detrend.mkruns.pro : globals and support macros : -*- sh -*-
-
-## XXX this script needs to be updated to use books, not queues
-## XXX this script needs to be reconsidered...
-
-## This script defines tasks which regularly initiate the detrend
-## runs.  Each detrend type is given its own task, which are run once
-## (or twice) per day to build the relevant detrend data
-
-## when one of these tasks is run, it creates a new detrend run ID in
-## the database, and selects the exposures for that run on the basis
-## of the specified filters
-
-# create a new detrend bias-creation run
-task mkdetruns.megacam
-  # command      dettool -select -time NOW -24h -type bias -camera megacam
-  command      mkdetruns.megacam
-  host         local
-
-  periods      -poll 1
-  periods      -exec 60
-  periods      -timeout 30
-  trange        18:00 19:00 -nmax 1
-
-  # success: a new detrend run was created
-  task.exit    0
-    echo "new MEGACAM detrend runs"
-    queueprint stdout
-  end
-
-  # 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
-
-  # operation timed out?  is the database down?
-  task.exit timeout
-    # send someone email?
-    echo "unable to define a new detrend run"
-  end
-end
-
-# create a new detrend bias-creation run
-task mkdetruns.isp
-  # command      dettool -select -time NOW -24h -type bias -camera megacam
-  command      mkdetruns.isp
-  host         local
-
-  periods      -poll 1
-  periods      -exec 60
-  periods      -timeout 30
-  trange        18:00 19:00 -nmax 1
-
-  # success: a new detrend run was created
-  task.exit    0
-    echo "new ISP detrend runs"
-    queueprint stdout
-  end
-
-  # 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
-
-  # operation timed out?  is the database down?
-  task.exit timeout
-    # send someone email?
-    echo "unable to define a new detrend run"
-  end
-end
-
-# create a new detrend bias-creation run
-task mkdetruns.gpc
-  # command      dettool -select -time NOW -24h -type bias -camera megacam
-  command      mkdetruns.gpc
-  host         local
-
-  periods      -poll 1
-  periods      -exec 60
-  periods      -timeout 30
-  trange        18:00 19:00 -nmax 1
-
-  # success: a new detrend run was created
-  task.exit    0
-    echo "new GPC detrend runs"
-    queueprint stdout
-  end
-
-  # 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
-
-  # operation timed out?  is the database down?
-  task.exit timeout
-    # send someone email?
-    echo "unable to define a new detrend run"
-  end
-end
Index: /branches/cnb_branches/cnb_branch_20090301/ippTasks/detrend.norm.pro
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTasks/detrend.norm.pro	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTasks/detrend.norm.pro	(revision 23594)
@@ -9,8 +9,4 @@
 book init detPendingNormImfile
 book init detPendingNormExp
-
-book init detCleanupNormStatImfile
-book init detCleanupNormImfile
-book init detCleanupNormExp
 
 macro detnorm.reset
@@ -18,8 +14,4 @@
   book init detPendingNormImfile
   book init detPendingNormExp
-
-  book init detCleanupNormStatImfile
-  book init detCleanupNormImfile
-  book init detCleanupNormExp
 end
 
@@ -28,8 +20,4 @@
   book listbook detPendingNormImfile
   book listbook detPendingNormExp
-
-  book listbook detCleanupNormStatImfile
-  book listbook detCleanupNormImfile
-  book listbook detCleanupNormExp
 end
 
@@ -53,23 +41,4 @@
     active true
   end
-
-  task detrend.cleanup.norm.load
-    active true
-  end
-  task detrend.cleanup.norm.run
-    active true
-  end
-  task detrend.cleanup.normexp.load
-    active true
-  end
-  task detrend.cleanup.normexp.run
-    active true
-  end
-  task detrend.cleanup.normstat.load
-    active true
-  end
-  task detrend.cleanup.normstat.run
-    active true
-  end
 end
 
@@ -91,22 +60,4 @@
   end
   task detrend.normstat.run
-    active false
-  end
-  task detrend.cleanup.norm.load
-    active false
-  end
-  task detrend.cleanup.norm.run
-    active false
-  end
-  task detrend.cleanup.normexp.load
-    active false
-  end
-  task detrend.cleanup.normexp.run
-    active false
-  end
-  task detrend.cleanup.normstat.load
-    active false
-  end
-  task detrend.cleanup.normstat.run
     active false
   end
@@ -117,8 +68,4 @@
 $detPendingNormImfile_DB = 0
 $detPendingNormExp_DB = 0
-
-$detCleanupNormStatImfile_DB = 0
-$detCleanupNormImfile_DB = 0
-$detCleanupNormExp_DB = 0
 
 # select images ready for copy 
@@ -493,349 +440,2 @@
   end
 end
-
-########## cleanup normstat ###########
-task	       detrend.cleanup.normstat.load
-  host         local
-
-  periods      -poll $LOADPOLL
-  periods      -exec $LOADEXEC
-  periods      -timeout 30
-  npending     1
-  active       true
-
-  stdout NULL
-  stderr $LOGDIR/detrend.cleanup.normstat.log
-
-  task.exec
-    $run = dettool -pendingcleanup_normalizedstat
-    if ($DB:n == 0)
-      option DEFAULT
-    else
-      # save the DB name for the exit tasks
-      option $DB:$detCleanupNormStatImfile_DB
-      $run = $run -dbname $DB:$detCleanupNormStatImfile_DB
-      $detCleanupNormStatImfile_DB ++
-      if ($detCleanupNormStatImfile_DB >= $DB:n) set detCleanupNormStatImfile_DB = 0
-    end
-    add_poll_args run
-    command $run
-  end
-
-  # success
-  task.exit    0
-    # convert 'stdout' to book format
-    ipptool2book stdout detCleanupNormStatImfile -key det_id:iteration -uniq -setword dbname $options:0 -setword pantaskState INIT
-    if ($VERBOSE > 2)
-      book listbook detCleanupNormStatImfile
-    end
-
-    # delete existing entries in the appropriate pantaskStates
-    process_cleanup detCleanupNormStatImfile
-  end
-
-  # locked list
-  task.exit    default
-    showcommand failure
-  end
-
-  task.exit    crash
-    showcommand crash
-  end
-
-  # operation times out?
-  task.exit    timeout
-    showcommand timeout
-  end
-end
-
-# run the ipp_cleanup.pl script on pending images
-task	       detrend.cleanup.normstat.run
-  periods      -poll $RUNPOLL
-  periods      -exec $RUNEXEC
-  periods      -timeout 60
-  active       true
-
-  task.exec
-    book npages detCleanupNormStatImfile -var N
-    if ($N == 0) break
-    if ($NETWORK == 0) break
-    
-    # look for new images in detCleanupNormStatImfile (pantaskState == INIT)
-    book getpage detCleanupNormStatImfile 0 -var pageName -key pantaskState INIT
-    if ("$pageName" == "NULL") break
-
-    book setword detCleanupNormStatImfile $pageName pantaskState RUN
-    book getword detCleanupNormStatImfile $pageName det_id   -var DET_ID   
-    book getword detCleanupNormStatImfile $pageName iteration -var ITERATION
-    book getword detCleanupNormStatImfile $pageName camera -var CAMERA
-    book getword detCleanupNormStatImfile $pageName state -var CLEANUP_MODE
-    book getword detCleanupNormStatImfile $pageName dbname -var DBNAME
-
-    # specify choice of local or remote host based on camera and chip (class_id)
-    set.host.for.camera $CAMERA FPA
-
-    stdout $LOGDIR/detrend.cleanup.normstat.log
-    stderr $LOGDIR/detrend.cleanup.normstat.log
-
-    # XXX is everything listed here needed?
-    $run = ipp_cleanup.pl --stage detrend.normstat.imfile --stage_id $DET_ID --iteration $ITERATION --camera $CAMERA --mode $CLEANUP_MODE
-    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 detCleanupNormStatImfile $options:0 $JOB_STATUS
-  end
-
-  task.exit    crash
-    showcommand crash
-    book setword detCleanupNormStatImfile $options:0 pantaskState CRASH
-  end
-
-  # operation timed out?
-  task.exit    timeout
-    showcommand timeout
-    book setword detCleanupNormStatImfile $options:0 pantaskState TIMEOUT
-  end
-end
- 
-########## cleanup norm (normalized.imfile) ###########
-task	       detrend.cleanup.norm.load
-  host         local
-
-  periods      -poll $LOADPOLL
-  periods      -exec $LOADEXEC
-  periods      -timeout 30
-  npending     1
-  active       true
-
-  stdout NULL
-  stderr $LOGDIR/detrend.cleanup.norm.log
-
-  task.exec
-    $run = dettool -pendingcleanup_normalizedimfile
-    if ($DB:n == 0)
-      option DEFAULT
-    else
-      # save the DB name for the exit tasks
-      option $DB:$detCleanupNormImfile_DB
-      $run = $run -dbname $DB:$detCleanupNormImfile_DB
-      $detCleanupNormImfile_DB ++
-      if ($detCleanupNormImfile_DB >= $DB:n) set detCleanupNormImfile_DB = 0
-    end
-    add_poll_args run
-    command $run
-  end
-
-  # success
-  task.exit    0
-    # convert 'stdout' to book format
-    ipptool2book stdout detCleanupNormImfile -key det_id:iteration:class_id -uniq -setword dbname $options:0 -setword pantaskState INIT
-    if ($VERBOSE > 2)
-      book listbook detCleanupNormImfile
-    end
-
-    # delete existing entries in the appropriate pantaskStates
-    process_cleanup detCleanupNormImfile
-  end
-
-  # locked list
-  task.exit    default
-    showcommand failure
-  end
-
-  task.exit    crash
-    showcommand crash
-  end
-
-  # operation times out?
-  task.exit    timeout
-    showcommand timeout
-  end
-end
-
-# run the ipp_cleanup.pl script on pending images
-task	       detrend.cleanup.norm.run
-  periods      -poll $RUNPOLL
-  periods      -exec $RUNEXEC
-  periods      -timeout 60
-  active       true
-
-  task.exec
-    book npages detCleanupNormImfile -var N
-    if ($N == 0) break
-    if ($NETWORK == 0) break
-    
-    # look for new images in detCleanupNormImfile (pantaskState == INIT)
-    book getpage detCleanupNormImfile 0 -var pageName -key pantaskState INIT
-    if ("$pageName" == "NULL") break
-
-    book setword detCleanupNormImfile $pageName pantaskState RUN
-    book getword detCleanupNormImfile $pageName det_id   -var DET_ID   
-    book getword detCleanupNormImfile $pageName iteration -var ITERATION
-    book getword detCleanupNormImfile $pageName class_id -var CLASS_ID 
-    book getword detCleanupNormImfile $pageName camera -var CAMERA
-    book getword detCleanupNormImfile $pageName state -var CLEANUP_MODE
-    book getword detCleanupNormImfile $pageName dbname -var DBNAME
-
-    # specify choice of local or remote host based on camera and chip (class_id)
-    set.host.for.camera $CAMERA FPA
-
-    stdout $LOGDIR/detrend.cleanup.norm.log
-    stderr $LOGDIR/detrend.cleanup.norm.log
-
-    # XXX is everything listed here needed?
-    $run = ipp_cleanup.pl --stage detrend.norm.imfile --stage_id $DET_ID --iteration $ITERATION --class_id $CLASS_ID --camera $CAMERA --mode $CLEANUP_MODE
-    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 detCleanupNormImfile $options:0 $JOB_STATUS
-  end
-
-  task.exit    crash
-    showcommand crash
-    book setword detCleanupNormImfile $options:0 pantaskState CRASH
-  end
-
-  # operation timed out?
-  task.exit    timeout
-    showcommand timeout
-    book setword detCleanupNormImfile $options:0 pantaskState TIMEOUT
-  end
-end
- 
-########## cleanup normexp ###########
-task	       detrend.cleanup.normexp.load
-  host         local
-
-  periods      -poll $LOADPOLL
-  periods      -exec $LOADEXEC
-  periods      -timeout 30
-  npending     1
-  active       true
-
-  stdout NULL
-  stderr $LOGDIR/detrend.normexp.cleanup.log
-
-  task.exec
-    $run = dettool -pendingcleanup_normalizedexp
-    if ($DB:n == 0)
-      option DEFAULT
-    else
-      # save the DB name for the exit tasks
-      option $DB:$detCleanupNormExp_DB
-      $run = $run -dbname $DB:$detCleanupNormExp_DB
-      $detCleanupNormExp_DB ++
-      if ($detCleanupNormExp_DB >= $DB:n) set detCleanupNormExp_DB = 0
-    end
-    add_poll_args run
-    command $run
-  end
-
-  # success
-  task.exit    0
-    # convert 'stdout' to book format
-    ipptool2book stdout detCleanupNormExp -key det_id:iteration -uniq -setword dbname $options:0 -setword pantaskState INIT
-    if ($VERBOSE > 2)
-      book listbook detCleanupNormExp
-    end
-
-    # delete existing entries in the appropriate pantaskStates
-    process_cleanup detCleanupNormExp
-  end
-
-  # locked list
-  task.exit    default
-    showcommand failure
-  end
-
-  task.exit    crash
-    showcommand crash
-  end
-
-  # operation times out?
-  task.exit    timeout
-    showcommand timeout
-  end
-end
-
-# run the ipp_cleanup.pl script on pending images
-task	       detrend.cleanup.normexp.run
-  periods      -poll $RUNPOLL
-  periods      -exec $RUNEXEC
-  periods      -timeout 60
-  active       true
-
-  task.exec
-    book npages detCleanupNormExp -var N
-    if ($N == 0) break
-    if ($NETWORK == 0) break
-    
-    # look for new images in detCleanupNormExp (pantaskState == INIT)
-    book getpage detCleanupNormExp 0 -var pageName -key pantaskState INIT
-    if ("$pageName" == "NULL") break
-
-    book setword detCleanupNormExp $pageName pantaskState RUN
-    book getword detCleanupNormExp $pageName det_id   -var DET_ID   
-    book getword detCleanupNormExp $pageName iteration -var ITERATION
-    book getword detCleanupNormExp $pageName camera -var CAMERA
-    book getword detCleanupNormExp $pageName state -var CLEANUP_MODE
-    book getword detCleanupNormExp $pageName dbname -var DBNAME
-
-    # specify choice of local or remote host based on camera and chip (class_id)
-    set.host.for.camera $CAMERA FPA
-
-    stdout $LOGDIR/detrend.cleanup.normexp.log
-    stderr $LOGDIR/detrend.cleanup.normexp.log
-
-    # XXX is everything listed here needed?
-    $run = ipp_cleanup.pl --stage detrend.norm.exp --stage_id $DET_ID --iteration $ITERATION --camera $CAMERA --mode $CLEANUP_MODE
-    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 detCleanupNormExp $options:0 $JOB_STATUS
-  end
-
-  task.exit    crash
-    showcommand crash
-    book setword detCleanupNormExp $options:0 pantaskState CRASH
-  end
-
-  # operation timed out?
-  task.exit    timeout
-    showcommand timeout
-    book setword detCleanupNormExp $options:0 pantaskState TIMEOUT
-  end
-end
- 
Index: /branches/cnb_branches/cnb_branch_20090301/ippTasks/detrend.process.pro
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTasks/detrend.process.pro	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTasks/detrend.process.pro	(revision 23594)
@@ -8,12 +8,8 @@
 book init detPendingProcessedImfile
 book init detPendingProcessedExp
-book init detCleanupProcessedImfile
-book init detCleanupProcessedExp
 
 macro detproc.reset
   book init detPendingProcessedImfile
   book init detPendingProcessedExp
-  book init detCleanupProcessedImfile
-  book init detCleanupProcessedExp
 end
 
@@ -23,8 +19,4 @@
   echo detPendingProcessedExp
   book listbook detPendingProcessedExp
-  echo detCleanupProcessedImfile
-  book listbook detCleanupProcessedImfile
-  echo detCleanupProcessedExp
-  book listbook detCleanupProcessedExp
 end
 
@@ -42,16 +34,4 @@
     active true
   end
-  task detrend.cleanup.process.load
-    active true
-  end
-  task detrend.cleanup.process.run
-    active true
-  end
-  task detrend.cleanup.processexp.load
-    active true
-  end
-  task detrend.cleanup.processexp.run
-    active true
-  end
 end
 
@@ -69,16 +49,4 @@
     active false
   end
-  task detrend.cleanup.process.load
-    active false
-  end
-  task detrend.cleanup.process.run
-    active false
-  end
-  task detrend.cleanup.processexp.load
-    active false
-  end
-  task detrend.cleanup.processexp.run
-    active false
-  end
 end
 
@@ -87,6 +55,4 @@
 $detPendingProcessedImfile_DB = 0
 $detPendingProcessedExp_DB = 0
-$detCleanupProcessedImfile_DB = 0
-$detCleanupProcessedExp_DB = 0
 
 # select images ready for copy 
@@ -347,238 +313,2 @@
   end
 end
-
-######## cleanup process imfile ########
-task	       detrend.cleanup.process.load
-  host         local
-
-  periods      -poll $LOADPOLL
-  periods      -exec $LOADEXEC
-  periods      -timeout 30
-  npending     1
-  active       true
-
-  stdout NULL
-  stderr $LOGDIR/detrend.cleanup.process.imfile.log
-
-  task.exec
-    $run = dettool -pendingcleanup_processedimfile
-    if ($DB:n == 0)
-      option DEFAULT
-    else
-      # save the DB name for the exit tasks
-      option $DB:$detCleanupProcessedImfile_DB
-      $run = $run -dbname $DB:$detCleanupProcessedImfile_DB
-      $detCleanupProcessedImfile_DB ++
-      if ($detCleanupProcessedImfile_DB >= $DB:n) set detCleanupProcessedImfile_DB = 0
-    end
-    add_poll_args run
-    command $run
-  end
-
-  # success
-  task.exit    0
-    # convert 'stdout' to book format
-    ipptool2book stdout detCleanupProcessedImfile -key det_id:exp_id:class_id -uniq -setword dbname $options:0 -setword pantaskState INIT
-    if ($VERBOSE > 2)
-      book listbook detCleanupProcessedImfile
-    end
-
-    # delete existing entries in the appropriate pantaskStates
-    process_cleanup detCleanupProcessedImfile
-  end
-
-  # locked list
-  task.exit    default
-    showcommand failure
-  end
-
-  task.exit    crash
-    showcommand crash
-  end
-
-  # operation times out?
-  task.exit    timeout
-    showcommand timeout
-  end
-end
-
-# run the ipp_cleanup.pl script on pending images
-task	       detrend.cleanup.process.run
-  periods      -poll $RUNPOLL
-  periods      -exec $RUNEXEC
-  periods      -timeout 60
-  active       true
-
-  task.exec
-    book npages detCleanupProcessedImfile -var N
-    if ($N == 0) break
-    if ($NETWORK == 0) break
-    
-    # look for new images in detCleanupProcessedImfile (pantaskState == INIT)
-    book getpage detCleanupProcessedImfile 0 -var pageName -key pantaskState INIT
-    if ("$pageName" == "NULL") break
-
-    book setword detCleanupProcessedImfile $pageName pantaskState RUN
-    book getword detCleanupProcessedImfile $pageName det_id   -var DET_ID   
-    book getword detCleanupProcessedImfile $pageName exp_id   -var EXP_ID   
-    book getword detCleanupProcessedImfile $pageName class_id -var CLASS_ID 
-    book getword detCleanupProcessedImfile $pageName camera   -var CAMERA
-    book getword detCleanupProcessedImfile $pageName state    -var CLEANUP_MODE
-    book getword detCleanupProcessedImfile $pageName dbname   -var DBNAME
-
-    # specify choice of local or remote host based on camera and chip (class_id)
-    set.host.for.camera $CAMERA FPA
-
-    stdout $LOGDIR/detrend.cleanup.process.imfile.log
-    stderr $LOGDIR/detrend.cleanup.process.imfile.log
-
-    # XXX is everything listed here needed?
-    $run = ipp_cleanup.pl --stage detrend.process.imfile --stage_id $DET_ID --exp_id $EXP_ID --class_id $CLASS_ID --camera $CAMERA --mode $CLEANUP_MODE
-    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 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
-
-  # operation timed out?
-  task.exit    timeout
-    showcommand timeout
-    book setword detCleanupProcessedImfile $options:0 pantaskState TIMEOUT
-  end
-end
- 
-
-######## cleanup process exp ########
-task	       detrend.cleanup.processexp.load
-  host         local
-
-  periods      -poll $LOADPOLL
-  periods      -exec $LOADEXEC
-  periods      -timeout 30
-  npending     1
-  active       true
-
-  stdout NULL
-  stderr $LOGDIR/detrend.cleanup.process.exp.log
-
-  task.exec
-    $run = dettool -pendingcleanup_processedexp
-    if ($DB:n == 0)
-      option DEFAULT
-    else
-      # save the DB name for the exit tasks
-      option $DB:$detCleanupProcessedExp_DB
-      $run = $run -dbname $DB:$detCleanupProcessedExp_DB
-      $detCleanupProcessedExp_DB ++
-      if ($detCleanupProcessedExp_DB >= $DB:n) set detCleanupProcessedExp_DB = 0
-    end
-    add_poll_args run
-    command $run
-  end
-
-  # success
-  task.exit    0
-    # convert 'stdout' to book format
-    ipptool2book stdout detCleanupProcessedExp -key det_id:exp_id -uniq -setword dbname $options:0 -setword pantaskState INIT
-    if ($VERBOSE > 2)
-      book listbook detCleanupProcessedExp
-    end
-
-    # delete existing entries in the appropriate pantaskStates
-    process_cleanup detCleanupProcessedExp
-  end
-
-  # locked list
-  task.exit    default
-    showcommand failure
-  end
-
-  task.exit    crash
-    showcommand crash
-  end
-
-  # operation times out?
-  task.exit    timeout
-    showcommand timeout
-  end
-end
-
-# run the ipp_cleanup.pl script on pending images
-task	       detrend.cleanup.processexp.run
-  periods      -poll $RUNPOLL
-  periods      -exec $RUNEXEC
-  periods      -timeout 60
-  active       true
-
-  task.exec
-    book npages detCleanupProcessedExp -var N
-    if ($N == 0) break
-    if ($NETWORK == 0) break
-    
-    # look for new images in detCleanupProcessedExp (pantaskState == INIT)
-    book getpage detCleanupProcessedExp 0 -var pageName -key pantaskState INIT
-    if ("$pageName" == "NULL") break
-
-    book setword detCleanupProcessedExp $pageName pantaskState RUN
-    book getword detCleanupProcessedExp $pageName det_id   -var DET_ID   
-    book getword detCleanupProcessedExp $pageName exp_id   -var EXP_ID   
-    book getword detCleanupProcessedExp $pageName camera -var CAMERA
-    book getword detCleanupProcessedExp $pageName state -var CLEANUP_MODE
-    book getword detCleanupProcessedExp $pageName dbname -var DBNAME
-
-    # specify choice of local or remote host based on camera and chip (class_id)
-    set.host.for.camera $CAMERA FPA
-
-    stdout $LOGDIR/detrend.cleanup.process.exp.log
-    stderr $LOGDIR/detrend.cleanup.process.exp.log
-
-    # XXX is everything listed here needed?
-    $run = ipp_cleanup.pl --stage detrend.process.exp --stage_id $DET_ID --exp_id $EXP_ID --camera $CAMERA --mode $CLEANUP_MODE
-    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 detCleanupProcessedExp $options:0 $JOB_STATUS
-  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
-    showcommand timeout
-    book setword detCleanupProcessedExp $options:0 pantaskState TIMEOUT
-  end
-end
Index: /branches/cnb_branches/cnb_branch_20090301/ippTasks/detrend.resid.pro
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTasks/detrend.resid.pro	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTasks/detrend.resid.pro	(revision 23594)
@@ -8,12 +8,8 @@
 book init detPendingResidImfile
 book init detPendingResidExp
-book init detCleanupResidImfile
-book init detCleanupResidExp
 
 macro detresid.reset
   book init detPendingResidImfile
   book init detPendingResidExp
-  book init detCleanupResidImfile
-  book init detCleanupResidExp
 end
 
@@ -21,6 +17,4 @@
   book listbook detPendingResidImfile
   book listbook detPendingResidExp
-  book listbook detCleanupResidImfile
-  book listbook detCleanupResidExp
 end
 
@@ -38,16 +32,4 @@
     active true
   end
-  task detrend.cleanup.resid.load
-    active true
-  end
-  task detrend.cleanup.resid.run
-    active true
-  end
-  task detrend.cleanup.residexp.load
-    active true
-  end
-  task detrend.cleanup.residexp.run
-    active true
-  end
 end
 
@@ -65,16 +47,4 @@
     active false
   end
-  task detrend.cleanup.resid.load
-    active false
-  end
-  task detrend.cleanup.resid.run
-    active false
-  end
-  task detrend.cleanup.residexp.load
-    active false
-  end
-  task detrend.cleanup.residexp.run
-    active false
-  end
 end
 
@@ -82,6 +52,4 @@
 $detPendingResidImfile_DB = 0
 $detPendingResidExp_DB = 0
-$detCleanupResidImfile_DB = 0
-$detCleanupResidExp_DB = 0
 
 # select images ready for copy 
@@ -348,240 +316,2 @@
   end
 end
-
-######## cleanup resid imfile ########
-task	       detrend.cleanup.resid.load
-  host         local
-
-  periods      -poll $LOADPOLL
-  periods      -exec $LOADEXEC
-  periods      -timeout 30
-  npending     1
-  active       true
-
-  stdout NULL
-  stderr $LOGDIR/detrend.cleanup.resid.imfile.log
-
-  task.exec
-    $run = dettool -pendingcleanup_residimfile
-    if ($DB:n == 0)
-      option DEFAULT
-    else
-      # save the DB name for the exit tasks
-      option $DB:$detCleanupResidImfile_DB
-      $run = $run -dbname $DB:$detCleanupResidImfile_DB
-      $detCleanupResidImfile_DB ++
-      if ($detCleanupResidImfile_DB >= $DB:n) set detCleanupResidImfile_DB = 0
-    end
-    add_poll_args run
-    command $run
-  end
-
-  # success
-  task.exit    0
-    # convert 'stdout' to book format
-    ipptool2book stdout detCleanupResidImfile -key det_id:iteration:exp_id:class_id -uniq -setword dbname $options:0 -setword pantaskState INIT
-    if ($VERBOSE > 2)
-      book listbook detCleanupResidImfile
-    end
-
-    # delete existing entries in the appropriate pantaskStates
-    process_cleanup detCleanupResidImfile
-  end
-
-  # locked list
-  task.exit    default
-    showcommand failure
-  end
-
-  task.exit    crash
-    showcommand crash
-  end
-
-  # operation times out?
-  task.exit    timeout
-    showcommand timeout
-  end
-end
-
-# run the ipp_cleanup.pl script on pending images
-task	       detrend.cleanup.resid.run
-  periods      -poll $RUNPOLL
-  periods      -exec $RUNEXEC
-  periods      -timeout 60
-  active       true
-
-  task.exec
-    book npages detCleanupResidImfile -var N
-    if ($N == 0) break
-    if ($NETWORK == 0) break
-    
-    # look for new images in detCleanupResidImfile (pantaskState == INIT)
-    book getpage detCleanupResidImfile 0 -var pageName -key pantaskState INIT
-    if ("$pageName" == "NULL") break
-
-    book setword detCleanupResidImfile $pageName pantaskState RUN
-    book getword detCleanupResidImfile $pageName det_id   -var DET_ID   
-    book getword detCleanupResidImfile $pageName exp_id   -var EXP_ID   
-    book getword detCleanupResidImfile $pageName class_id -var CLASS_ID 
-    book getword detCleanupResidImfile $pageName iteration -var ITERATION     
-    book getword detCleanupResidImfile $pageName camera -var CAMERA
-    book getword detCleanupResidImfile $pageName state -var CLEANUP_MODE
-    book getword detCleanupResidImfile $pageName dbname -var DBNAME
-
-    # specify choice of local or remote host based on camera and chip (class_id)
-    set.host.for.camera $CAMERA FPA
-
-    stdout $LOGDIR/detrend.cleanup.resid.imfile.log
-    stderr $LOGDIR/detrend.cleanup.resid.imfile.log
-
-    # XXX is everything listed here needed?
-    $run = ipp_cleanup.pl --stage detrend.resid.imfile --stage_id $DET_ID --exp_id $EXP_ID --class_id $CLASS_ID --iteration $ITERATION --camera $CAMERA --mode $CLEANUP_MODE
-    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 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
-
-  # operation timed out?
-  task.exit    timeout
-    showcommand timeout
-    book setword detCleanupResidImfile $options:0 pantaskState TIMEOUT
-  end
-end
- 
-
-######## cleanup resid exp ########
-task	       detrend.cleanup.residexp.load
-  host         local
-
-  periods      -poll $LOADPOLL
-  periods      -exec $LOADEXEC
-  periods      -timeout 30
-  npending     1
-  active       true
-
-  stdout NULL
-  stderr $LOGDIR/detrend.cleanup.resid.exp.log
-
-  task.exec
-    $run = dettool -pendingcleanup_residexp
-    if ($DB:n == 0)
-      option DEFAULT
-    else
-      # save the DB name for the exit tasks
-      option $DB:$detCleanupResidExp_DB
-      $run = $run -dbname $DB:$detCleanupResidExp_DB
-      $detCleanupResidExp_DB ++
-      if ($detCleanupResidExp_DB >= $DB:n) set detCleanupResidExp_DB = 0
-    end
-    add_poll_args run
-    command $run
-  end
-
-  # success
-  task.exit    0
-    # convert 'stdout' to book format
-    ipptool2book stdout detCleanupResidExp -key det_id:iteration:exp_id -uniq -setword dbname $options:0 -setword pantaskState INIT
-    if ($VERBOSE > 2)
-      book listbook detCleanupResidExp
-    end
-
-    # delete existing entries in the appropriate pantaskStates
-    process_cleanup detCleanupResidExp
-  end
-
-  # locked list
-  task.exit    default
-    showcommand failure
-  end
-
-  task.exit    crash
-    showcommand crash
-  end
-
-  # operation times out?
-  task.exit    timeout
-    showcommand timeout
-  end
-end
-
-# run the ipp_cleanup.pl script on pending images
-task	       detrend.cleanup.residexp.run
-  periods      -poll $RUNPOLL
-  periods      -exec $RUNEXEC
-  periods      -timeout 60
-  active       true
-
-  task.exec
-    book npages detCleanupResidExp -var N
-    if ($N == 0) break
-    if ($NETWORK == 0) break
-    
-    # look for new images in detCleanupResidExp (pantaskState == INIT)
-    book getpage detCleanupResidExp 0 -var pageName -key pantaskState INIT
-    if ("$pageName" == "NULL") break
-
-    book setword detCleanupResidExp $pageName pantaskState RUN
-    book getword detCleanupResidExp $pageName det_id    -var DET_ID   
-    book getword detCleanupResidExp $pageName exp_id    -var EXP_ID   
-    book getword detCleanupResidExp $pageName iteration -var ITERATION
-    book getword detCleanupResidExp $pageName camera 	-var CAMERA
-    book getword detCleanupResidExp $pageName state  	-var CLEANUP_MODE
-    book getword detCleanupResidExp $pageName dbname 	-var DBNAME
-
-    # specify choice of local or remote host based on camera and chip (class_id)
-    set.host.for.camera $CAMERA FPA
-
-    stdout $LOGDIR/detrend.cleanup.resid.exp.log
-    stderr $LOGDIR/detrend.cleanup.resid.exp.log
-
-    # XXX is everything listed here needed?
-    $run = ipp_cleanup.pl --stage detrend.resid.exp --stage_id $DET_ID --exp_id $EXP_ID --class_id $CLASS_ID --camera $CAMERA --mode $CLEANUP_MODE
-    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 detCleanupResidExp $options:0 $JOB_STATUS
-  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
-    showcommand timeout
-    book setword detCleanupResidExp $options:0 pantaskState TIMEOUT
-  end
-end
Index: /branches/cnb_branches/cnb_branch_20090301/ippTasks/detrend.stack.pro
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTasks/detrend.stack.pro	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTasks/detrend.stack.pro	(revision 23594)
@@ -7,9 +7,7 @@
 
 book init detPendingStackedImfile
-book init detCleanupStackedImfile
 
 macro detstack.reset
   book init detPendingStackedImfile
-  book init detCleanupStackedImfile
 end
 
@@ -17,6 +15,4 @@
   echo detPendingStackedImfile
   book listbook detPendingStackedImfile
-  echo detCleanupStackedImfile
-  book listbook detCleanupStackedImfile
 end
 
@@ -26,10 +22,4 @@
   end
   task detrend.stack.run
-    active true
-  end
-  task detrend.cleanup.stack.load
-    active true
-  end
-  task detrend.cleanup.stack.run
     active true
   end
@@ -43,15 +33,8 @@
     active false
   end
-  task detrend.cleanup.stack.load
-    active false
-  end
-  task detrend.cleanup.stack.run
-    active false
-  end
 end
 
 # this variable will cycle through the known database names
 $detPendingStackedImfile_DB = 0
-$detCleanupStackedImfile_DB = 0
 
 # select images ready for detrend_stack.pl
@@ -182,121 +165,2 @@
   end
 end
-
-########## cleanup stack ###########
-task	       detrend.cleanup.stack.load
-  host         local
-
-  periods      -poll $LOADPOLL
-  periods      -exec $LOADEXEC
-  periods      -timeout 30
-  npending     1
-  active       true
-
-  stdout NULL
-  stderr $LOGDIR/detrend.cleanup.stack.log
-
-  task.exec
-    $run = dettool -pendingcleanup_stacked
-    if ($DB:n == 0)
-      option DEFAULT
-    else
-      # save the DB name for the exit tasks
-      option $DB:$detCleanupStackedImfile_DB
-      $run = $run -dbname $DB:$detCleanupStackedImfile_DB
-      $detCleanupStackedImfile_DB ++
-      if ($detCleanupStackedImfile_DB >= $DB:n) set detCleanupStackedImfile_DB = 0
-    end
-    add_poll_args run
-    command $run
-  end
-
-  # success
-  task.exit    0
-    # convert 'stdout' to book format
-    ipptool2book stdout detCleanupStackedImfile -key det_id:iteration:class_id -uniq -setword dbname $options:0 -setword pantaskState INIT
-    if ($VERBOSE > 2)
-      book listbook detCleanupStackedImfile
-    end
-
-    # delete existing entries in the appropriate pantaskStates
-    process_cleanup detCleanupStackedImfile
-  end
-
-  # locked list
-  task.exit    default
-    showcommand failure
-  end
-
-  task.exit    crash
-    showcommand crash
-  end
-
-  # operation times out?
-  task.exit    timeout
-    showcommand timeout
-  end
-end
-
-# run the ipp_cleanup.pl script on pending images
-task	       detrend.cleanup.stack.run
-  periods      -poll $RUNPOLL
-  periods      -exec $RUNEXEC
-  periods      -timeout 60
-  active       true
-
-  task.exec
-    book npages detCleanupStackedImfile -var N
-    if ($N == 0) break
-    if ($NETWORK == 0) break
-    
-    # look for new images in detCleanupStackedImfile (pantaskState == INIT)
-    book getpage detCleanupStackedImfile 0 -var pageName -key pantaskState INIT
-    if ("$pageName" == "NULL") break
-
-    book setword detCleanupStackedImfile $pageName pantaskState RUN
-    book getword detCleanupStackedImfile $pageName det_id   -var DET_ID   
-    book getword detCleanupStackedImfile $pageName iteration -var ITERATION
-    book getword detCleanupStackedImfile $pageName class_id -var CLASS_ID 
-    book getword detCleanupStackedImfile $pageName camera -var CAMERA
-    book getword detCleanupStackedImfile $pageName state -var CLEANUP_MODE
-    book getword detCleanupStackedImfile $pageName dbname -var DBNAME
-
-    # specify choice of local or remote host based on camera and chip (class_id)
-    set.host.for.camera $CAMERA FPA
-
-    stdout $LOGDIR/detrend.cleanup.stack.log
-    stderr $LOGDIR/detrend.cleanup.stack.log
-
-    # XXX is everything listed here needed?
-    $run = ipp_cleanup.pl --stage detrend.stack.imfile --stage_id $DET_ID --iteration $ITERATION --class_id $CLASS_ID --camera $CAMERA --mode $CLEANUP_MODE
-    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 detCleanupStackedImfile $options:0 $JOB_STATUS
-  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
-    showcommand timeout
-    book setword detCleanupStackedImfile $options:0 pantaskState TIMEOUT
-  end
-end
- 
Index: /branches/cnb_branches/cnb_branch_20090301/ippTasks/dist.pro
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTasks/dist.pro	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTasks/dist.pro	(revision 23594)
@@ -134,4 +134,15 @@
     book getword distToProcess $pageName outroot -var OUTROOT
     book getword distToProcess $pageName dbname -var DBNAME
+    if ("$CLEAN" = "T")
+        $CLEAN_ARG = "--clean"
+    else
+        $CLEAN_ARG = ""
+    end
+    # magicked is output as integer due to the union in the sql
+    if ($MAGICKED)
+        $MAGICKED_ARG = "--magicked"
+    else
+        $MAGICKED_ARG = ""
+    end
 
 #    set.host.for.camera $CAMERA $MAGIC_ID
@@ -141,5 +152,5 @@
     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
+    $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_ARG $CLEAN_ARG --outroot $OUTROOT --logfile $logfile
 
     add_standard_args run
Index: /branches/cnb_branches/cnb_branch_20090301/ippTasks/nebulous.site.pro.in
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTasks/nebulous.site.pro.in	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTasks/nebulous.site.pro.in	(revision 23594)
@@ -1,6 +1,7 @@
 # customize these variables for your site
+# and copy the file to share/pantasks/modules directory in your installation
 
-$NEB_DB    = nebulous
-$NEB_HOST  = alala
+$NEB_DB    = your_nebulous_database_name
+$NEB_HOST  = host_for_nebulous_mysql_database
 $NEB_USER  = XXX
 $NEB_PASS  = XXX
Index: /branches/cnb_branches/cnb_branch_20090301/ippTasks/pantasks.pro
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTasks/pantasks.pro	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTasks/pantasks.pro	(revision 23594)
@@ -13,5 +13,5 @@
 $KEEP_FAILURES = 0
 
-$LOADPOLL = 0.25
+$LOADPOLL = 1.0
 $LOADEXEC = 5
 $RUNPOLL = 0.5
Index: /branches/cnb_branches/cnb_branch_20090301/ippTasks/replicate.pro
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTasks/replicate.pro	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTasks/replicate.pro	(revision 23594)
@@ -74,6 +74,6 @@
 
   # modify these after the tasks are tested
-  periods      -poll 10
-  periods      -exec 10
+  periods      -poll 0.5
+  periods      -exec 5
   periods      -timeout 1500
   npending     1
@@ -84,17 +84,35 @@
 
   task.exec
+      book npages replicatePending -var N
+      if ($N > 2000)
+        process_cleanup replicatePending
+        break
+      end      
+
       # 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
       # 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
-
-  end
-
-  # success
-  task.exit $EXIT_SUCCESS
+      command neb-admin --host $NEB_HOST --db $NEB_DB --user $NEB_USER --pass $NEB_PASS --pendingreplicate --limit 500 --so_id_start $SO_ID_START --so_id_range $SO_ID_RANGE
+      # periods      -exec 1800
+  end
+
+  # success : 0 -- we did not hit the limit, advance so_id counter
+  task.exit 0
     # advance the so_id counter
-    $SO_ID_START += $SO_ID_RANGE
-
+    $SO_ID_START = $SO_ID_START + $SO_ID_RANGE
+
+    # convert 'stdout' to book format
+    ipptool2book stdout replicatePending -key key -uniq -setword pantaskState INIT
+
+    if ($VERBOSE > 2)
+      book listbook replicatePending
+    end
+
+    # delete existing entries in the appropriate pantaskStates
+    process_cleanup replicatePending
+  end
+
+  # success : 1 -- we DID hit the limit, do NOT advance so_id counter 
+  task.exit 1
     # convert 'stdout' to book format
     ipptool2book stdout replicatePending -key key -uniq -setword pantaskState INIT
@@ -131,6 +149,6 @@
 # create the desired replicas
 task	       replicate.run
-  periods      -poll $RUNPOLL
-  periods      -exec 30
+  periods      -poll 0.5
+  periods      -exec 5
   periods      -timeout 30
 
@@ -139,5 +157,5 @@
     if ($NETWORK == 0) break
     if ($N == 0)
-        periods -exec 30
+        periods -exec 5
         break
     end
Index: /branches/cnb_branches/cnb_branch_20090301/ippTasks/simtest.ctemask.auto
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTasks/simtest.ctemask.auto	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTasks/simtest.ctemask.auto	(revision 23594)
@@ -0,0 +1,39 @@
+
+automate MULTI
+
+# we run in the sequence BLOCK -> CHECK -> LAUNCH -> BLOCK -> CHECK
+# success on CHECK -> LAUNCH
+# failure on BLOCK -> CHECK
+
+# XXX these steps all refer to "@DBNAME@"; they need to be more generic, even for the basic simtest analysis
+
+automate METADATA
+  name       STR BIAS
+  check      STR "regtool -processedexp -exp_type BIAS -inst @CAMERA@ -dbname @DBNAME@"
+  ncheck     S32 5
+  launch     STR "dettool -definebyquery -workdir file://@CWD@/detwork -inst @CAMERA@ -det_type BIAS -select_exp_type BIAS -dbname @DBNAME@"
+  block      STR "dettool -runs -active -det_type BIAS -dbname @DBNAME@"
+END
+
+automate METADATA
+  name       STR DARK
+  check      STR "detselect -search -inst @CAMERA@ -det_type BIAS -dbname @DBNAME@"
+  launch     STR "dettool -definebyquery -workdir file://@CWD@/detwork -inst @CAMERA@ -det_type DARK -select_exp_type DARK -dbname @DBNAME@"
+  block      STR "dettool -runs -active -det_type DARK -dbname @DBNAME@"
+END
+ 
+automate METADATA
+  name       STR SHUTTER
+  check      STR "detselect -search -inst @CAMERA@ -det_type DARK -dbname @DBNAME@"
+  launch     STR "dettool -definebyquery -workdir file://@CWD@/detwork -inst @CAMERA@ -det_type SHUTTER -filter r -select_exp_type FLAT -select_filter r -dbname @DBNAME@"
+  block      STR "dettool -runs -active -det_type SHUTTER -dbname @DBNAME@"
+END
+
+automate METADATA
+  name       STR FLAT-r
+  check      STR "detselect -search -inst @CAMERA@ -det_type SHUTTER -dbname @DBNAME@"
+  launch     STR "dettool -definebyquery -workdir file://@CWD@/detwork -inst @CAMERA@ -det_type FLAT -filter r -select_exp_type FLAT -select_filter r -dbname @DBNAME@"
+  block      STR "dettool -runs -active -det_type FLAT -filter r -dbname @DBNAME@"
+END
+
+# XXX let's build the necessary data, then see how to run the cte mask generation (input is flattened flats)
Index: /branches/cnb_branches/cnb_branch_20090301/ippTasks/simtest.ctemask.config
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTasks/simtest.ctemask.config	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTasks/simtest.ctemask.config	(revision 23594)
@@ -0,0 +1,38 @@
+
+# very basic test to generate flats and test the CTE masking process
+# define the simulated data to be generated
+SEQUENCE MULTI
+
+SEQUENCE METADATA
+  OBSTYPE    STR BIAS
+  CAMERA     STR SIMTEST
+  NIMAGES    S32  5
+END
+
+SEQUENCE METADATA
+  OBSTYPE    STR DARK
+  CAMERA     STR SIMTEST
+  @EXPTIMES  F32  30.0, 300.0
+  @NIMAGES   S32  3,    3
+END
+
+SEQUENCE METADATA
+  OBSTYPE    STR FLAT
+  CAMERA     STR SIMTEST
+
+  FILTERS    STR r,r,r,r,r
+  @EXPTIMES  F32 0.5,1.0,2.0,5.0,10.0
+
+  NSETUP     S32 1
+END
+
+SEQUENCE METADATA
+  OBSTYPE    STR FLAT
+  CAMERA     STR SIMTEST
+
+  FILTERS    STR r
+  @EXPTIMES  F32 20.0
+
+  NSETUP     S32 7
+END
+
Index: /branches/cnb_branches/cnb_branch_20090301/ippTasks/simtest.pro
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTasks/simtest.pro	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTasks/simtest.pro	(revision 23594)
@@ -45,4 +45,5 @@
     echo "  simtest.setup.detverify : run detrend creation and detrend verification"
     echo "  simtest.setup.flatcorr : run a flat-field correction demonstration"
+    echo "  simtest.setup.ctemask : run a ctemask demonstration"
     break
   end
@@ -126,4 +127,10 @@
 end
 
+macro simtest.setup.ctemask
+  $PPSIM_RECIPE = BADCTE.TEST
+  $SIMTEST_SEQUENCE = simtest.ctemask.config
+  $SIMTEST_AUTO = simtest.ctemask.auto
+end
+
 macro simtest.setup.flatcorr
   $PPSIM_RECIPE = FLATCORR
Index: /branches/cnb_branches/cnb_branch_20090301/ippTasks/summit.copy.pro
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTasks/summit.copy.pro	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTasks/summit.copy.pro	(revision 23594)
@@ -27,4 +27,6 @@
 # list of summit imfiles that need to be downloaded
 book init pzPendingImfile
+# list of pzDownloadExps that have completed downloading
+book init pzPendingAdvance
 
 macro copy.on
@@ -50,4 +52,7 @@
       active true
   end
+  task summit.toadvance
+      active true
+  end
   task summit.advance
       active true
@@ -75,4 +80,7 @@
   end
   task pztool.clearfault
+      active false
+  end
+  task summit.toadvance
       active false
   end
@@ -86,4 +94,5 @@
 $pztoolPendingExp_DB = 0
 $pztoolPendingImfile_DB = 0
+$pztoolPendingAdvance_DB = 0
 $pztoolClearFault_DB = 0
 $pztoolAdvance_DB = 0;
@@ -433,5 +442,5 @@
 	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
+        $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 --dbname $DBNAME --timeout 120 --verbose --copies 2
 	if ($COMPRESS) 
             $run = $run --compress
@@ -510,48 +519,126 @@
 end
 
-# promote exposures for which all imfiles have been copied
-task	       summit.advance
-  host         local
-
-  periods      -poll $LOADPOLL
-  periods      -exec $LOADEXEC
-  periods      -timeout 30
-  npending     1
-
-  stdout NULL
-  stderr $LOGDIR/summit.advance.log
-
-  task.exec
-    $run = pztool -advance
-    if ($DB:n == 0)
-      option DEFAULT
-    else
-      # save the DB name for the exit tasks
-      option $DB:$pztoolAdvance_DB
-      $run = $run -dbname $DB:$pztoolAdvance_DB
-      $pztoolAdvance_DB ++
-      if ($pztoolAdvance_DB >= $DB:n) set pztoolAdvance_DB = 0
-    end
-    add_poll_args run
-    command $run
-  end
-
-  # success
-  task.exit    0
-  end
-
-  # locked list
-  task.exit    default
-    showcommand failure
-  end
-
-  task.exit    crash
-    showcommand crash
-  end
-
-  # operation times out?
-  task.exit    timeout
-    showcommand timeout
-  end
-end
-
+# build a book of exposures that have completed downloading
+task summit.toadvance
+    host         local
+
+    periods      -exec     30
+    periods      -poll      1
+    periods      -timeout  120
+    # trage       16:00 23:59
+    # trage       00:00 04:00
+    npending     1
+
+    # select entries from the current DB; cycle to the next DB, if it exists
+    # iff the DB list is not set, use the value defined in .ipprc
+    task.exec
+      if ($DB:n == 0)
+        option DEFAULT
+        command pztool -toadvance -limit 40
+      else
+        # save the DB name for the exit tasks
+        option $DB:$pztoolPendingAdvance_DB
+        command pztool -toadvance -limit 60 -dbname $DB:$pztoolPendingAdvance_DB
+        $pztoolPendingAdvance_DB ++
+        if ($pztoolPendingAdvance_DB >= $DB:n) set pztoolPendingAdvance_DB = 0
+      end
+    end
+  
+    # success
+    task.exit    0
+        # convert 'stdout' to book format
+        ipptool2book stdout pzPendingAdvance -key exp_name:camera:telescope -uniq -setword dbname $options:0 -setword pantaskState INIT
+	book shuffle pzPendingAdvance 
+
+        # delete existing entries in the appropriate pantaskStates
+        process_cleanup pzPendingAdvance
+    end
+
+    task.exit     default
+        showcommand failure
+    end
+    task.exit     crash
+        showcommand crash
+    end
+    task.exit     timeout
+        showcommand timeout
+    end
+end
+
+task summit.advance
+    periods      -exec     5
+    periods      -poll     0.05
+    periods      -timeout  650
+    # trage       16:00 23:59
+    # trage       00:00 04:00
+
+    task.exec
+        if ($NETWORK == 0) break
+
+        # if we are waiting on data, make the interval long
+        book npages pzPendingAdvance -var N
+        if ($N == 0)
+            periods -exec 20
+            break
+        end
+        periods -exec 0.05
+
+        # find an exp that needs imfiles fetched
+        book getpage pzPendingAdvance 0 -var pageName -key pantaskState INIT
+        if ("$pageName" == "NULL") break
+
+        # set that exp to run
+        book setword pzPendingAdvance $pageName pantaskState RUN
+
+        book getword pzPendingAdvance $pageName exp_name  -var EXP_NAME
+        book getword pzPendingAdvance $pageName camera    -var CAMERA
+        book getword pzPendingAdvance $pageName telescope -var TELESCOPE
+        book getword pzPendingAdvance $pageName dbname    -var DBNAME
+
+        # 2007-08-30T05:09:59Z
+        substr $DATEOBS 0 4 YEAR
+        substr $DATEOBS 5 2 MONTH
+        substr $DATEOBS 8 2 DAY
+
+        # we need to set the workdir based on 1) nebulous or not? 2) chip/host relationship
+        # this function uses workdir_template, default_host, volume_template, volume_default,
+        # it sets workdir and volume
+       	set.workdir.by.camera $CAMERA $CLASS_ID $workdir_template $default_host workdir_base
+
+        $workdir = $workdir_template/$CAMERA/$YEAR\$MONTH\$DAY
+
+	# workdir examples:
+	# file://data/@HOST@.0/gpc1/20080130
+	# neb://@HOST@.0/gpc1/20080130
+
+	stdout $LOGDIR/summit.advance.log
+	stderr $LOGDIR/summit.advance.log
+
+        $run = pztool -advance -exp_name $EXP_NAME -inst $CAMERA -telescope $TELESCOPE -end_stage reg -workdir $workdir -dbname $DBNAME
+
+        # store 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 pzPendingAdvance $options:0 $JOB_STATUS
+    end
+
+    task.exit crash
+        showcommand crash
+        book setword pzPendingAdvance $options:0 pantaskState CRASH
+    end 
+
+    # operation timed out?
+    task.exit timeout
+        showcommand timeout
+        book setword pzPendingAdvance $options:0 pantaskState TIMEOUT
+    end 
+end
Index: /branches/cnb_branches/cnb_branch_20090301/ippTasks/warp.pro
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTasks/warp.pro	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTasks/warp.pro	(revision 23594)
@@ -289,4 +289,11 @@
     book getword warpPendingSkyCell $pageName exp_tag -var EXP_TAG
     book getword warpPendingSkyCell $pageName state -var RUN_STATE
+    book getword warpPendingSkyCell $pageName magicked -var CHIP_MAGICKED
+    if ("$CHIP_MAGICKED" == "T")
+        $MAGICKED_ARG = "--magicked"
+    else
+        $MAGICKED_ARG = ""
+    end
+
 
     # set the host and workdir based on the skycell hash
@@ -300,5 +307,5 @@
     stderr $LOGDIR/warp.skycell.log
 
-    $run = warp_skycell.pl --threads @MAX_THREADS@ --warp_id $WARP_ID --warp_skyfile_id $WARP_SKYFILE_ID --skycell_id $SKYCELL_ID --tess_dir $TESS_DIR --camera $CAMERA --outroot $outroot --redirect-output --run-state $RUN_STATE
+    $run = warp_skycell.pl --threads @MAX_THREADS@ --warp_id $WARP_ID --warp_skyfile_id $WARP_SKYFILE_ID --skycell_id $SKYCELL_ID --tess_dir $TESS_DIR --camera $CAMERA --outroot $outroot --redirect-output --run-state $RUN_STATE $MAGICKED_ARG
     add_standard_args run
 
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/configure.ac
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/configure.ac	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/configure.ac	(revision 23594)
@@ -18,5 +18,5 @@
 PKG_CHECK_MODULES([PSLIB], [pslib >= 1.1.0])
 PKG_CHECK_MODULES([PSMODULES], [psmodules >= 1.1.0])
-PKG_CHECK_MODULES([IPPDB], [ippdb >= 1.1.48]) 
+PKG_CHECK_MODULES([IPPDB], [ippdb >= 1.1.50]) 
 
 PXTOOLS_CFLAGS="${PSLIB_CFLAGS=} ${PSMODULES_CFLAGS=} ${IPPDB_CFLAGS=}"
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/share/Makefile.am
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/share/Makefile.am	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/share/Makefile.am	(revision 23594)
@@ -76,4 +76,6 @@
      difftool_definebyquery_part2.sql \
      difftool_definebyquery_temp_create.sql \
+     difftool_definewarpwarp_select.sql \
+     difftool_definewarpwarp_insert.sql \
      difftool_donecleanup.sql \
      difftool_inputskyfile.sql \
@@ -152,4 +154,5 @@
      regtool_revertprocessedexp.sql \
      regtool_revertprocessedimfile.sql \
+     regtool_updateprocessedimfile.sql \
      stacktool_definebyquery_insert.sql \
      stacktool_definebyquery_insert_random_part1.sql \
@@ -172,4 +175,6 @@
      warptool_donecleanup.sql \
      warptool_exp.sql \
+     warptool_finished_run_select.sql \
+     warptool_finish_run.sql \
      warptool_imfile.sql \
      warptool_pendingcleanuprun.sql \
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/share/camtool_find_processedexp.sql
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/share/camtool_find_processedexp.sql	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/share/camtool_find_processedexp.sql	(revision 23594)
@@ -1,4 +1,5 @@
 SELECT DISTINCT
     camProcessedExp.*,
+    chipRun.chip_id,
     rawExp.exp_tag,
     rawExp.exp_name,
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 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/share/chiptool_change_imfile_data_state.sql	(revision 23594)
@@ -1,7 +1,10 @@
 -- handle changes in data_state. Used for the modes tocleanedimfile and topurgedimfile
--- args are new data_state, chip_id, class_id, and current expected state for chipRun
+-- args are new data_state, a possibly empty string for updating the magicked state,
+-- chip_id and class_id
 UPDATE chipProcessedImfile
+JOIN rawImfile USING(exp_id, class_id)
     SET 
-    data_state = '%s'
+    chipProcessedImfile.data_state = '%s'
+    -- set magicked hook %s
 WHERE
     chip_id = %lld
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/share/chiptool_completely_processed_exp.sql
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/share/chiptool_completely_processed_exp.sql	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/share/chiptool_completely_processed_exp.sql	(revision 23594)
@@ -11,10 +11,12 @@
     dvodb,
     tess_id,
-    end_stage
+    end_stage,
+    all_files_magicked as magicked
 FROM
     (SELECT
         chipRun.*,
         rawImfile.class_id as rawimfile_class_id,
-        chipProcessedImfile.class_id
+        chipProcessedImfile.class_id,
+        SUM(!chipProcessedImfile.magicked) = 0 as all_files_magicked
     FROM chipRun
     JOIN rawImfile
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/share/chiptool_pendingimfile.sql
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/share/chiptool_pendingimfile.sql	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/share/chiptool_pendingimfile.sql	(revision 23594)
@@ -4,4 +4,6 @@
     rawImfile.class_id,
     rawImfile.uri,
+    rawImfile.magicked as raw_magicked,
+    (rawImfile.user_1 is not NULL and rawImfile.user_1 > 0.5) as deburned,
     rawExp.exp_tag,
     rawExp.exp_name,
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/share/difftool_completed_runs.sql
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/share/difftool_completed_runs.sql	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/share/difftool_completed_runs.sql	(revision 23594)
@@ -1,8 +1,10 @@
 SELECT DISTINCT
-    diff_id
+    diff_id,
+    all_magicked as magicked
 FROM (
     SELECT
         COUNT(diffInputSkyfile.skycell_id), COUNT(diffSkyfile.skycell_id),
-        diffSkyfile.*
+        diffSkyfile.*,
+        SUM(!diffSkyfile.magicked) = 0 as all_magicked
     FROM diffRun
     JOIN diffInputSkyfile USING(diff_id)
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/share/difftool_definewarpwarp_insert.sql
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/share/difftool_definewarpwarp_insert.sql	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/share/difftool_definewarpwarp_insert.sql	(revision 23594)
@@ -0,0 +1,16 @@
+INSERT INTO diffInputSkyfile
+SELECT
+    %s,                         -- diff_id
+    skycell_id,
+    %s,                         -- input warp_id
+    NULL,                       -- input stack_id
+    %s,                         -- template warp_id
+    NULL,                       -- template stack_id
+    tess_id,
+    0                           -- diff_skyfile_id
+FROM warpSkyfile AS inputWarpSkyfile
+JOIN warpSkyfile AS templateWarpSkyfile USING(skycell_id, tess_id)
+WHERE inputWarpSkyfile.ignored = 0
+    AND templateWarpSkyfile.ignored = 0
+    AND inputWarpSkyfile.warp_id = %s
+    AND templateWarpSkyfile.warp_id = %s
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/share/difftool_definewarpwarp_select.sql
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/share/difftool_definewarpwarp_select.sql	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/share/difftool_definewarpwarp_select.sql	(revision 23594)
@@ -0,0 +1,25 @@
+SELECT
+    inputWarpRun.warp_id AS input_warp_id,
+    inputRawExp.exp_id AS input_exp_id,
+    inputWarpRun.tess_id AS tess_id,
+    SUBSTRING_INDEX(GROUP_CONCAT(templateWarpRun.warp_id ORDER BY ABS(ASIN(SQRT(POW(SIN(0.5*(inputRawExp.decl - templateRawExp.decl)),2) + COS(inputRawExp.decl) * COS(templateRawExp.decl) * POW(SIN(0.5*(inputRawExp.ra - templateRawExp.ra)),2))))), ',', 1) AS template_warp_id
+FROM warpRun AS inputWarpRun
+JOIN fakeRun AS inputFakeRun USING(fake_id)
+JOIN camRun AS inputCamRun USING(cam_id)
+JOIN chipRun AS inputChipRun USING(chip_id)
+JOIN rawExp AS inputRawExp USING(exp_id)
+-- To find exposures that haven't been diffed:%s LEFT JOIN diffRun ON diffRun.exp_id = inputRawExp.exp_id
+JOIN warpRun AS templateWarpRun
+    ON templateWarpRun.warp_id != inputWarpRun.warp_id -- Don't use self as template!
+    AND templateWarpRun.tess_id = inputWarpRun.tess_id -- Ensure using same tessellation
+JOIN fakeRun AS templateFakeRun
+    ON templateFakeRun.fake_id = templateWarpRun.fake_id
+JOIN camRun AS templateCamRun
+    ON templateCamRun.cam_id = templateFakeRun.cam_id
+JOIN chipRun AS templateChipRun
+    ON templateChipRun.chip_id = templateCamRun.chip_id
+JOIN rawExp AS templateRawExp
+    ON templateRawExp.exp_id = templateChipRun.exp_id
+    AND templateRawExp.filter = inputRawExp.filter
+-- WHERE hook %s
+GROUP BY input_warp_id
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/share/difftool_inputskyfile.sql
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/share/difftool_inputskyfile.sql	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/share/difftool_inputskyfile.sql	(revision 23594)
@@ -9,4 +9,5 @@
         warpSkyfile.uri,
         warpSkyfile.path_base,
+        warpSkyfile.magicked as magicked,
         0 as template,
         rawExp.camera
@@ -47,4 +48,5 @@
         warpSkyfile.uri,
         warpSkyfile.path_base,
+        warpSkyfile.magicked as magicked,
         1 as template,
         rawExp.camera
@@ -85,4 +87,5 @@
         stackSumSkyfile.uri,
         stackSumSkyfile.path_base,
+        0 as magicked,
         0 as template,
         rawExp.camera
@@ -117,4 +120,5 @@
         stackSumSkyfile.uri,
         stackSumSkyfile.path_base,
+        0 as magicked,
         1 as template,
         rawExp.camera
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/share/disttool_definebyquery_select.sql
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/share/disttool_definebyquery_select.sql	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/share/disttool_definebyquery_select.sql	(revision 23594)
@@ -0,0 +1,124 @@
+-- Maybe it would be better to split the query into multiple modes
+-- the way it is outstanding rawExp's to bundle will block other stages
+-- from being processed
+SELECT
+    stage,
+    stage_id,
+    obs_mode,
+    clean
+FROM (
+    SELECT DISTINCT     -- we use distinct to handle multiple chip runs. Distribution will find right one
+        'raw' AS stage,
+        rawExp.exp_id AS stage_id,
+        rawExp.obs_mode,
+        distTarget.clean
+    FROM rawExp
+    JOIN chipRun USING(exp_id)
+    JOIN distTarget ON distTarget.obs_mode = rawExp.obs_mode AND distTarget.stage = 'raw'
+    JOIN rcInterest USING(target_id)
+    LEFT JOIN distRun ON distRun.stage = 'raw' AND distRun.stage_id = exp_id
+    WHERE distTarget.state = 'enabled'
+        AND rcInterest.state = 'enabled'
+        AND chipRun.state = 'full'       -- Note: we need a completed chip stage magicDSRun because we
+                                         -- need the mask file to NAN masked pixels
+        -- to support cameras that don't need magic disttool adds the magicked restriction 
+        -- \nAND rawExp.magicked AND chipRun.magicked here:
+        -- raw magicked HOOK %s
+        AND distRun.dist_id IS NULL      -- no existing distRun 
+    -- raw where hook %s
+
+UNION
+    SELECT
+        'chip' as stage,
+        chipRun.chip_id as stage_id,
+        rawExp.obs_mode,
+        distTarget.clean
+    FROM chipRun
+    JOIN rawExp USING(exp_id)
+    JOIN distTarget ON distTarget.stage = 'chip' AND rawExp.obs_mode = distTarget.obs_mode 
+    JOIN rcInterest USING(target_id)
+    LEFT JOIN distRun ON distTarget.stage = 'chip' AND distRun.stage_id = chipRun.chip_id
+    WHERE distTarget.state = 'enabled'
+        AND rcInterest.state = 'enabled'
+        AND distRun.dist_id IS NULL      -- no existing distRun 
+        AND ((!distTarget.clean AND (chipRun.state = 'full')
+            -- If camera requires files to be magicked before distibution
+            -- the following gets added by disttool
+            AND chipRun.magicked 
+            -- chip magicked HOOK %s
+           ) 
+           OR (distTarget.clean AND chipRun.state = 'cleaned'))
+    -- chip where hook %s
+
+UNION
+    SELECT
+        'cam' as stage,
+        camRun.cam_id as stage_id,
+        rawExp.obs_mode,
+        distTarget.clean
+    FROM camRun 
+    JOIN chipRun USING(chip_id)
+    JOIN rawExp USING(exp_id)
+    JOIN distTarget ON distTarget.stage = 'cam' AND rawExp.obs_mode = distTarget.obs_mode
+    JOIN rcInterest USING(target_id)
+    LEFT JOIN distRun ON distRun.stage = 'cam' AND camRun.cam_id = distRun.stage_id
+    WHERE distTarget.state = 'enabled'
+        AND rcInterest.state = 'enabled'
+        AND distRun.dist_id IS NULL      -- no existing distRun 
+        AND ((!distTarget.clean  AND camRun.state = 'full'
+            -- need magicked chip run because camera mask are destreaked by chip destreaking
+            -- the following line
+            -- AND chipRun.magicked
+            -- cam magicked HOOK %s
+             )
+            OR (distTarget.clean AND camRun.state = 'cleaned')
+        )
+    -- cam where hook %s
+
+UNION
+    SELECT
+        'fake' as stage,
+        fakeRun.fake_id as stage_id,
+        rawExp.obs_mode,
+        distTarget.clean
+    FROM fakeRun
+    JOIN camRun USING(cam_id)
+    JOIN chipRun USING(chip_id)
+    JOIN rawExp USING(exp_id)
+    JOIN distTarget ON rawExp.obs_mode = distTarget.obs_mode AND distTarget.stage = 'fake'
+    JOIN rcInterest USING(target_id)
+    LEFT JOIN distRun ON distRun.stage = 'fake' AND camRun.cam_id = distRun.stage_id
+    WHERE distTarget.state = 'enabled'
+        AND rcInterest.state = 'enabled'
+        AND distRun.dist_id IS NULL      -- no existing distRun 
+        AND ((fakeRun.state = 'full') OR (distTarget.clean AND fakeRun.state = 'cleaned'))
+    -- fake where hook %s
+
+UNION
+    SELECT
+        'warp' as stage,
+        warpRun.warp_id AS stage_id,
+        rawExp.obs_mode,
+        distTarget.clean
+    FROM warpRun
+    JOIN fakeRun USING(fake_id)
+    JOIN camRun ON fakeRun.cam_id = camRun.cam_id
+    JOIN chipRun USING(chip_id)
+    JOIN rawExp USING(exp_id)
+    JOIN distTarget ON distTarget.stage = 'warp' AND rawExp.obs_mode = distTarget.obs_mode
+    JOIN rcInterest USING(target_id)
+    LEFT JOIN distRun ON distRun.stage = 'warp' AND (distRun.stage_id = warp_id)
+    WHERE  distTarget.state = 'enabled'
+        AND rcInterest.state = 'enabled'
+        AND distRun.dist_id IS NULL
+        AND ((warpRun.state = 'full' 
+            --  AND warpRun.magicked
+            -- warp magicked hook %s
+             ) 
+            OR (distTarget.clean AND warpRun.state = 'cleaned')
+        )
+        -- warp where hook %s
+) as foo
+
+
+
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/share/disttool_pendingcomponent.sql
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/share/disttool_pendingcomponent.sql	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/share/disttool_pendingcomponent.sql	(revision 23594)
@@ -2,4 +2,5 @@
 SELECT
     distRun.dist_id,
+    distRun.label,
     stage,
     stage_id,
@@ -8,5 +9,5 @@
     rawExp.camera,
     outroot,
-    rawImfile.uri as path_base,
+    rawImfile.uri as path_base,         -- change this once rawImfile has path_base
     chipProcessedImfile.path_base as chip_path_base,
     NULL as state,
@@ -15,8 +16,20 @@
 FROM distRun
 JOIN rawExp ON exp_id = stage_id
-JOIN rawImfile using(exp_id)
+JOIN (                      -- find the last magicked chip run 
+    SELECT
+        exp_id,
+        MAX(chip_id) AS chip_id
+    FROM chipRun
+    WHERE 
+        chipRun.state = 'full'
+        AND chipRun.exp_id = exp_id
+        --   AND chipRun.magicked
+        -- magicked hook 1 %s
+        GROUP BY exp_id
+    ) AS bestChipRun 
+    USING(exp_id)
+JOIN rawImfile USING(exp_id)
 JOIN chipProcessedImfile
-    ON distRun.chip_id = chipProcessedImfile.chip_id
-    AND rawImfile.class_id = chipProcessedImfile.class_id
+    USING(exp_id, chip_id, class_id)
 LEFT JOIN distComponent 
     ON distRun.dist_id = distComponent.dist_id 
@@ -26,4 +39,6 @@
     AND distRun.stage = 'raw'
     AND distComponent.dist_id IS NULL
+    -- if magicked add AND rawImfile.magicked here
+    -- where hook 1 %s
 
 -- chip stage
@@ -31,4 +46,5 @@
 SELECT
     distRun.dist_id,
+    distRun.label,
     stage,
     stage_id,
@@ -53,7 +69,67 @@
     AND distRun.stage = 'chip'
     AND distComponent.dist_id IS NULL
-UNION
-SELECT
-    distRun.dist_id,
+    -- where hook 2 %s
+UNION
+SELECT
+    distRun.dist_id,
+    distRun.label,
+    stage,
+    stage_id,
+    chipProcessedImfile.class_id AS component,
+    clean,
+    rawExp.camera,
+    outroot,
+    camProcessedExp.path_base,
+    chipProcessedImfile.path_base as chip_path_base,
+    camRun.state,
+    NULL,
+    0
+FROM distRun
+JOIN camRun ON camRun.cam_id = distRun.stage_id
+JOIN camProcessedExp USING(cam_id)
+JOIN chipRun USING(chip_id)
+JOIN chipProcessedImfile USING(exp_id, chip_id)
+JOIN rawExp using(exp_id)
+LEFT JOIN distComponent 
+    ON distRun.dist_id = distComponent.dist_id 
+    AND chipProcessedImfile.class_id = distComponent.component
+WHERE
+    distRun.state = 'new'
+    AND distRun.stage = 'camera'
+    AND distComponent.dist_id IS NULL
+    -- where hook 3 %s
+UNION
+SELECT
+    distRun.dist_id,
+    distRun.label,
+    stage,
+    stage_id,
+    fakeProcessedImfile.class_id AS component,
+    clean,
+    rawExp.camera,
+    outroot,
+    fakeProcessedImfile.path_base,
+    NULL,
+    fakeRun.state,
+    NULL,
+    0
+FROM distRun
+JOIN fakeRun ON fakeRun.fake_id = distRun.stage_id
+JOIN fakeProcessedImfile USING(fake_id)
+JOIN camRun USING(cam_id)
+JOIN chipRun USING(chip_id, exp_id)
+JOIN rawExp using(exp_id)
+LEFT JOIN distComponent 
+    ON distRun.dist_id = distComponent.dist_id 
+    AND fakeProcessedImfile.class_id = distComponent.component
+WHERE
+    distRun.state = 'new'
+    AND distRun.stage = 'fake'
+    AND distComponent.dist_id IS NULL
+    -- where hook 4 %s
+UNION
+SELECT
+    distRun.dist_id,
+    distRun.label,
     stage,
     stage_id,
@@ -81,7 +157,9 @@
     AND distRun.stage = 'warp'
     AND distComponent.dist_id IS NULL
-UNION
-SELECT
-    distRun.dist_id,
+    -- where hook 5 %s
+UNION
+SELECT
+    distRun.dist_id,
+    distRun.label,
     stage,
     stage_id,
@@ -108,7 +186,9 @@
     AND distRun.stage = 'diff'
     AND distComponent.dist_id IS NULL
+    -- where hook 6 %s
 UNION
 SELECT DISTINCT
     distRun.dist_id,
+    distRun.label,
     stage,
     stage_id,
@@ -152,3 +232,4 @@
     AND distRun.stage = 'stack'
     AND distComponent.dist_id IS NULL
+    -- where hook 7 %s
 ) as Foo
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/share/magicdstool_completed_runs.sql
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/share/magicdstool_completed_runs.sql	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/share/magicdstool_completed_runs.sql	(revision 23594)
@@ -12,5 +12,5 @@
         AND magicDSFile.component = rawImfile.class_id
     WHERE
-        magicDSRun.state = 'run'
+        magicDSRun.state = 'new'
         AND magicDSRun.stage = 'raw'
     GROUP BY
@@ -30,5 +30,5 @@
         AND magicDSFile.component = chipProcessedImfile.class_id
     WHERE
-        magicDSRun.state = 'run'
+        magicDSRun.state = 'new'
         AND magicDSRun.stage = 'chip'
     GROUP BY
@@ -48,5 +48,5 @@
         AND magicDSFile.component = warpSkyfile.skycell_id
     WHERE
-        magicDSRun.state = 'run'
+        magicDSRun.state = 'new'
         AND magicDSRun.stage = 'warp'
         AND warpSkyfile.fault = 0
@@ -72,5 +72,5 @@
         AND magicDSFile.component = diffSkyfile.skycell_id
     WHERE
-        magicDSRun.state = 'run'
+        magicDSRun.state = 'new'
         AND magicDSRun.stage = 'diff'
         AND diffSkyfile.fault = 0
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/share/magicdstool_todestreak.sql
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/share/magicdstool_todestreak.sql	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/share/magicdstool_todestreak.sql	(revision 23594)
@@ -26,5 +26,5 @@
     AND magicDSFile.component = rawImfile.class_id
 WHERE
-    magicDSRun.state = 'run'
+    magicDSRun.state = 'new'
     AND magicDSRun.stage = 'raw'
     AND magicDSFile.component IS NULL
@@ -57,5 +57,5 @@
     AND magicDSFile.component = chipProcessedImfile.class_id
 WHERE
-    magicDSRun.state = 'run'
+    magicDSRun.state = 'new'
     AND magicDSRun.stage = 'chip'
     AND chipRun.state = 'full'
@@ -91,5 +91,5 @@
     AND magicDSFile.component = warpSkyfile.skycell_id
 WHERE
-    magicDSRun.state = 'run'
+    magicDSRun.state = 'new'
     AND magicDSRun.stage = 'warp'
     AND warpRun.state = 'full'
@@ -127,5 +127,5 @@
     AND magicDSFile.component = diffSkyfile.skycell_id
 WHERE
-    magicDSRun.state = 'run'
+    magicDSRun.state = 'new'
     AND magicDSRun.stage = 'diff'
     AND diffSkyfile.fault = 0
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/share/magictool_addmask.sql
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/share/magictool_addmask.sql	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/share/magictool_addmask.sql	(revision 23594)
@@ -2,5 +2,5 @@
     magicRun
 SET
-    state = 'stop'
+    state = 'full'
 WHERE
-    state != 'stop'
+    state != 'full'
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/share/magictool_chipprocessedimfile.sql
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/share/magictool_chipprocessedimfile.sql	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/share/magictool_chipprocessedimfile.sql	(revision 23594)
@@ -26,4 +26,4 @@
     chipRun.state = 'full'
     AND chipProcessedImfile.fault = 0
---   AND magicRun.state = 'stop'
+--   AND magicRun.state = 'full'
 --   AND magicMask.fault = 0
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/share/magictool_diffskyfile.sql
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/share/magictool_diffskyfile.sql	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/share/magictool_diffskyfile.sql	(revision 23594)
@@ -24,4 +24,4 @@
     diffRun.state = 'full'
     AND diffSkyfile.fault = 0
---   AND magicRun.state = 'stop'
+--   AND magicRun.state = 'full'
 --   AND magicMask.fault = 0
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/share/magictool_inputskyfile.sql
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/share/magictool_inputskyfile.sql	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/share/magictool_inputskyfile.sql	(revision 23594)
@@ -9,3 +9,3 @@
     AND magicInputSkyfile.node = diffSkyfile.skycell_id
 WHERE
-    magicRun.state = 'run'
+    magicRun.state = 'new'
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/share/magictool_mask.sql
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/share/magictool_mask.sql	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/share/magictool_mask.sql	(revision 23594)
@@ -6,4 +6,4 @@
     USING(magic_id)
 WHERE
-    magicRun.state = 'stop'
+    magicRun.state = 'full'
     AND magicMask.fault = 0
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/share/magictool_rawimfile.sql
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/share/magictool_rawimfile.sql	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/share/magictool_rawimfile.sql	(revision 23594)
@@ -24,4 +24,4 @@
 WHERE
     rawImfile.fault = 0
---   AND magicRun.state = 'stop'
+--   AND magicRun.state = 'full'
 --   AND magicMask.fault = 0
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/share/magictool_tomask.sql
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/share/magictool_tomask.sql	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/share/magictool_tomask.sql	(revision 23594)
@@ -10,5 +10,5 @@
 LEFT JOIN magicNodeResult USING(magic_id, node)
 WHERE
-    magicRun.state = 'run'
+    magicRun.state = 'new'
     AND magicNodeResult.node = 'root'
     AND magicNodeResult.fault = 0
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/share/magictool_toprocess_inputs.sql
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/share/magictool_toprocess_inputs.sql	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/share/magictool_toprocess_inputs.sql	(revision 23594)
@@ -19,5 +19,5 @@
     AND magicTree.node = magicNodeResult.node
 WHERE
-    magicRun.state = 'run'
+    magicRun.state = 'new'
     AND magicNodeResult.magic_id IS NULL
     AND magicNodeResult.node IS NULL
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/share/magictool_toprocess_tree.sql
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/share/magictool_toprocess_tree.sql	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/share/magictool_toprocess_tree.sql	(revision 23594)
@@ -15,5 +15,5 @@
     AND magicTree.node = magicNodeResult.node
 WHERE
-    magicRun.state = 'run'
+    magicRun.state = 'new'
     -- where hook %s
 ORDER BY
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/share/magictool_toskyfilemask.sql
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/share/magictool_toskyfilemask.sql	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/share/magictool_toskyfilemask.sql	(revision 23594)
@@ -7,4 +7,4 @@
     USING(magic_id)
 WHERE
-    magicRun.state = 'run'
+    magicRun.state = 'new'
     AND magicSkyfileMask.magic_id IS NULL
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/share/magictool_totree.sql
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/share/magictool_totree.sql	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/share/magictool_totree.sql	(revision 23594)
@@ -12,5 +12,5 @@
     USING(magic_id)
 WHERE
-    magicRun.state = 'run'
+    magicRun.state = 'new'
     AND magicTree.node IS NULL
     AND magicRun.fault = 0
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/share/magictool_warpskyfile.sql
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/share/magictool_warpskyfile.sql	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/share/magictool_warpskyfile.sql	(revision 23594)
@@ -21,4 +21,4 @@
     warpRun.state = 'full'
     AND warpSkyfile.fault = 0
---   AND magicRun.state = 'stop'
+--   AND magicRun.state = 'full'
 --   AND magicMask.fault = 0
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 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/share/pxadmin_create_tables.sql	(revision 23594)
@@ -167,4 +167,5 @@
     fault SMALLINT NOT NULL,
     epoch TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+    magicked TINYINT,
     PRIMARY KEY(exp_id),
     KEY(exp_name),
@@ -263,4 +264,5 @@
     tess_id VARCHAR(64),
     end_stage VARCHAR(64),
+    magicked TINYINT,
     PRIMARY KEY(chip_id),
     KEY(chip_id), KEY(exp_id),
@@ -826,4 +828,5 @@
     end_stage VARCHAR(64),
     registered DATETIME,
+    magicked TINYINT,
     PRIMARY KEY(warp_id),
     KEY(warp_id),
@@ -963,4 +966,5 @@
         tess_id VARCHAR(64),
         exp_id  BIGINT,
+        magicked TINYINT,
         PRIMARY KEY(diff_id),
         KEY(diff_id),
@@ -1095,4 +1099,5 @@
         stage_id BIGINT,
         cam_id BIGINT,
+        label VARCHAR(64),
         outroot VARCHAR(255),
         recoveryroot VARCHAR(255),
@@ -1102,4 +1107,6 @@
         KEY(magic_ds_id),
         KEY(state),
+        KEY(magic_id),
+        KEY(label),
         FOREIGN KEY (magic_id)  REFERENCES  magicRun(magic_id)
 ) ENGINE=innodb DEFAULT CHARSET=latin1;
@@ -1225,3 +1232,106 @@
         KEY(job_id),
         FOREIGN KEY (req_id)  REFERENCES  pstampRequest(req_id)
-) ENGINE=innodb DEFAULT CHARSET=latin1
+) ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE distTarget (
+    target_id   BIGINT AUTO_INCREMENT, 
+    obs_mode    VARCHAR(64),
+    stage       VARCHAR(64),
+    clean       TINYINT,
+    state       VARCHAR(64),
+    comment     VARCHAR(255),
+    PRIMARY KEY(target_id)
+)  ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE distRun (
+    dist_id     BIGINT AUTO_INCREMENT,
+    target_id   BIGINT,
+    stage       VARCHAR(64),
+    stage_id    BIGINT,
+    label       VARCHAR(64),
+    outroot     VARCHAR(255),
+    clean       TINYINT,
+    state       VARCHAR(64),
+    time_stamp  TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+    fault       SMALLINT,
+    PRIMARY KEY(dist_id),
+    KEY(state),
+    KEY(label),
+    FOREIGN KEY(target_id) REFERENCES distTarget(target_id)
+)  ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE distComponent (
+    dist_id     BIGINT, 
+    component   VARCHAR(64),
+    bytes       INT,
+    md5sum      VARCHAR(32),
+    state       VARCHAR(64),
+    name        VARCHAR(255),
+    fault       SMALLINT,
+    PRIMARY KEY(dist_id, component),
+    KEY(state),
+    FOREIGN KEY(dist_id) REFERENCES distRun(dist_id)
+)  ENGINE=innodb DEFAULT CHARSET=latin1;
+
+
+CREATE TABLE rcDSProduct (
+    prod_id     BIGINT AUTO_INCREMENT,
+    name        VARCHAR(64),
+    dbname      VARCHAR(64),
+    dbhost      VARCHAR(64),
+    prod_root   VARCHAR(255),
+    PRIMARY KEY(prod_id)
+)  ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE rcDestination (
+    dest_id     BIGINT AUTO_INCREMENT,
+    prod_id     BIGINT,
+    name        VARCHAR(64),
+    status_uri  VARCHAR(255),
+    comment     VARCHAR(255),
+    last_fileset VARCHAR(255),
+    state       VARCHAR(64),
+    PRIMARY KEY(dest_id),
+    FOREIGN KEY(prod_id) REFERENCES rcDSProduct(prod_id)
+)  ENGINE=innodb DEFAULT CHARSET=latin1;
+
+
+CREATE TABLE rcInterest (
+    int_id      BIGINT AUTO_INCREMENT,
+    dest_id     BIGINT,
+    target_id   BIGINT,
+    prod_id     BIGINT,
+    state       VARCHAR(64),
+    PRIMARY KEY(int_id),
+    FOREIGN KEY(dest_id) REFERENCES rcDestination(dest_id),
+    FOREIGN KEY(target_id) REFERENCES distTarget(target_id),
+    FOREIGN KEY(prod_id) REFERENCES rcDSProduct(prod_id)
+)  ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE rcDSFileset (
+    fs_id       BIGINT AUTO_INCREMENT,
+    dist_id     BIGINT,
+    prod_id     BIGINT,
+    name        VARCHAR(64),
+    state       VARCHAR(64),
+    registered  TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+    PRIMARY KEY(fs_id),
+    FOREIGN KEY(dist_id) REFERENCES distRun(dist_id),
+    FOREIGN KEY(prod_id) REFERENCES rcDSProduct(prod_id)
+)  ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE rcRun (
+    rc_id       BIGINT AUTO_INCREMENT,
+    fs_id       BIGINT,
+    dest_id     BIGINT,
+    state       VARCHAR(64),
+    registered  TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+    PRIMARY KEY(rc_id),
+    FOREIGN KEY(fs_id) REFERENCES rcDSFileset(fs_id),
+    FOREIGN KEY(dest_id) REFERENCES rcDestination(dest_id)
+)  ENGINE=innodb DEFAULT CHARSET=latin1;
+
+
+
+-- This comment line is here to avoid empty query error. 
+-- Another way to avoid that problem is to omit the semicolon above but I think that is untidy.
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/share/pxadmin_drop_tables.sql
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/share/pxadmin_drop_tables.sql	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/share/pxadmin_drop_tables.sql	(revision 23594)
@@ -54,4 +54,5 @@
 DROP TABLE IF EXISTS calRun;
 DROP TABLE IF EXISTS flatcorrRun;
+DROP TABLE IF EXISTS flatcorrExp;
 DROP TABLE IF EXISTS flatcorrChipLink;
 DROP TABLE IF EXISTS flatcorrCamLink;
@@ -60,3 +61,12 @@
 DROP TABLE IF EXISTS pstampRequest;
 DROP TABLE IF EXISTS pstampJob;
+DROP TABLE IF EXISTS distRun;
+DROP TABLE IF EXISTS distTarget;
+DROP TABLE IF EXISTS distComponent;
+DROP TABLE IF EXISTS rcDSProduct;
+DROP TABLE IF EXISTS rcDestination;
+DROP TABLE IF EXISTS rcInterest;
+DROP TABLE IF EXISTS rcDSFileset;
+DROP TABLE IF EXISTS rcRun;
+
 SET FOREIGN_KEY_CHECKS=1
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/share/pztool_find_completed_exp.sql
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/share/pztool_find_completed_exp.sql	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/share/pztool_find_completed_exp.sql	(revision 23594)
@@ -1,8 +1,7 @@
 SELECT DISTINCT
-    exp_name, -- return should match pzDownloadExp
+    exp_name,
     camera,
     telescope,
-    state,
-    NULL as epoch    -- epoch
+    state
 FROM (
     SELECT
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/share/regtool_updateprocessedimfile.sql
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/share/regtool_updateprocessedimfile.sql	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/share/regtool_updateprocessedimfile.sql	(revision 23594)
@@ -0,0 +1,6 @@
+UPDATE rawImfile
+  SET user_1 = %lf
+  WHERE
+    rawImfile.exp_id = %lld
+  AND
+    rawImfile.class_id = '%s'
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 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/share/warptool_change_skyfile_data_state.sql	(revision 23594)
@@ -8,6 +8,2 @@
     warp_id = %lld
     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'
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/share/warptool_definebyquery.sql
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/share/warptool_definebyquery.sql	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/share/warptool_definebyquery.sql	(revision 23594)
@@ -9,4 +9,5 @@
         rawExp.dateobs,
         rawExp.exp_tag,
+        rawExp.exp_name,
         rawExp.exp_type,
         rawExp.filelevel,
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/share/warptool_finish_run.sql
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/share/warptool_finish_run.sql	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/share/warptool_finish_run.sql	(revision 23594)
@@ -0,0 +1,5 @@
+UPDATE warpRun
+    SET state = 'full',
+    magicked = %d
+WHERE warp_id = %lld
+
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/share/warptool_finished_run_select.sql
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/share/warptool_finished_run_select.sql	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/share/warptool_finished_run_select.sql	(revision 23594)
@@ -0,0 +1,22 @@
+SELECT
+   warp_id,
+   magicked
+FROM
+   (SELECT DISTINCT
+       warpRun.warp_id,
+       warpSkyCellMap.warp_id as foo,
+       warpSkyfile.warp_id as bar,
+       SUM(!warpSkyfile.magicked) = 0 as magicked
+   FROM warpRun
+   JOIN warpSkyCellMap
+       USING(warp_id)
+   LEFT JOIN warpSkyfile
+       USING(warp_id, skycell_id)
+   WHERE
+       warpRun.state = 'new'
+   GROUP BY
+       warpRun.warp_id
+   HAVING
+       COUNT(warpSkyCellMap.warp_id) = COUNT(warpSkyfile.warp_id)
+       AND SUM(warpSkyfile.fault) = 0
+ ) as Foo
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/share/warptool_pendingcleanuprun.sql
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/share/warptool_pendingcleanuprun.sql	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/share/warptool_pendingcleanuprun.sql	(revision 23594)
@@ -13,3 +13,3 @@
 USING (exp_id)
 WHERE
-    (warpRun.state = 'goto_cleaned' OR warpRun.state = 'goto_purged')
+    (warpRun.state = 'goto_cleaned' OR warpRun.state = 'goto_scrubbed' OR warpRun.state = 'goto_purged')
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/share/warptool_pendingcleanupskyfile.sql
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/share/warptool_pendingcleanupskyfile.sql	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/share/warptool_pendingcleanupskyfile.sql	(revision 23594)
@@ -10,5 +10,7 @@
     USING(warp_id)
 WHERE
-    (warpRun.state = 'goto_cleaned' AND warpSkyfile.data_state = 'full')
+   ((warpRun.state = 'goto_cleaned'  AND warpSkyfile.data_state = 'full')
     OR
-    (warpRun.state = 'goto_purged' AND warpSkyfile.data_state != 'purged')
+    (warpRun.state = 'goto_scrubbed' AND warpSkyfile.data_state = 'full')
+    OR
+    (warpRun.state = 'goto_purged'   AND warpSkyfile.data_state != 'purged'))
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/share/warptool_towarped.sql
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/share/warptool_towarped.sql	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/share/warptool_towarped.sql	(revision 23594)
@@ -9,5 +9,6 @@
     rawExp.camera,
     rawExp.exp_tag,
-    warpRun.workdir
+    warpRun.workdir,
+    chipRun.magicked
 FROM warpRun
 JOIN warpSkyCellMap
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/src/Makefile.am
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/src/Makefile.am	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/src/Makefile.am	(revision 23594)
@@ -6,4 +6,5 @@
 	dettool \
 	difftool \
+	disttool \
 	flatcorr \
 	magictool \
@@ -45,4 +46,5 @@
 	dettool.h \
 	difftool.h \
+	disttool.h \
 	flatcorr.h \
 	faketool.h \
@@ -152,4 +154,10 @@
     difftoolConfig.c
 
+disttool_CFLAGS = $(PSLIB_CFLAGS) $(PSMODULES_CFLAGS) $(IPPDB_CFLAGS)
+disttool_LDADD = $(PSLIB_LIBS) $(PSMODULES_LIBS) $(IPPDB_LIBS) libpxtools.la
+disttool_SOURCES = \
+    disttool.c \
+    disttoolConfig.c
+
 stacktool_CFLAGS = $(PSLIB_CFLAGS) $(PSMODULES_CFLAGS) $(IPPDB_CFLAGS)
 stacktool_LDADD = $(PSLIB_LIBS) $(PSMODULES_LIBS) $(IPPDB_LIBS) libpxtools.la
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/src/camtool.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/src/camtool.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/src/camtool.c	(revision 23594)
@@ -109,5 +109,6 @@
     psMetadata *where = psMetadataAlloc();
     pxcamGetSearchArgs (config, where);
-    PXOPT_COPY_STR(config->args, where, "-label", "label", "==");
+    PXOPT_COPY_STR(config->args, where, "-label",     "chipRun.label",     "==");
+    PXOPT_COPY_STR(config->args, where, "-reduction", "chipRun.reduction", "==");
 
     if (!psListLength(where->list)
@@ -223,8 +224,9 @@
 
     psMetadata *where = psMetadataAlloc();
-    PXOPT_COPY_S64(config->args, where, "-cam_id", "cam_id", "==");
     pxcamGetSearchArgs (config, where);
-    PXOPT_COPY_STR(config->args, where, "-label", "camRun.label", "==");
-    PXOPT_COPY_STR(config->args, where, "-state", "camRun.state", "==");
+    PXOPT_COPY_S64(config->args, where, "-cam_id",    "camRun.cam_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-label",     "camRun.label", "==");
+    PXOPT_COPY_STR(config->args, where, "-state",     "camRun.state", "==");
+    PXOPT_COPY_STR(config->args, where, "-reduction", "camRun.reduction", "==");
 
     if (!psListLength(where->list)
@@ -271,7 +273,8 @@
 
     psMetadata *where = psMetadataAlloc();
-    PXOPT_COPY_S64(config->args, where, "-cam_id", "cam_id", "==");
     pxcamGetSearchArgs (config, where);
-    PXOPT_COPY_STR(config->args, where, "-label", "label", "==");
+    PXOPT_COPY_S64(config->args, where, "-cam_id",    "camRun.cam_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-label",     "camRun.label", "==");
+    PXOPT_COPY_STR(config->args, where, "-reduction", "camRun.reduction", "==");
 
     PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
@@ -334,10 +337,12 @@
 
     psMetadata *where = psMetadataAlloc();
-    PXOPT_COPY_S64(config->args, where, "-cam_id",   "cam_id",   "==");
-    PXOPT_COPY_S64(config->args, where, "-chip_id",  "chip_id",  "==");
-    PXOPT_COPY_STR(config->args, where, "-class",    "class",    "==");
-    PXOPT_COPY_STR(config->args, where, "-class_id", "class_id", "==");
     pxcamGetSearchArgs (config, where);
-    PXOPT_COPY_STR(config->args, where, "-label", "camRun.label", "==");
+    PXOPT_COPY_S64(config->args, where, "-cam_id",    "camRun.cam_id",                "==");
+    PXOPT_COPY_STR(config->args, where, "-label",     "camRun.label",                 "==");
+    PXOPT_COPY_STR(config->args, where, "-reduction", "camRun.reduction",             "==");
+    PXOPT_COPY_S64(config->args, where, "-chip_id",   "chipRun.chip_id",              "==");
+    PXOPT_COPY_STR(config->args, where, "-class_id",  "chipProcessedImfile.class_id", "==");
+
+    // XXX is this used? PXOPT_COPY_STR(config->args, where, "-class",    "class",         "==");
 
     PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
@@ -649,7 +654,8 @@
     // generate restrictions
     psMetadata *where = psMetadataAlloc();
-    PXOPT_COPY_S64(config->args, where, "-cam_id",   "cam_id",   "==");
     pxcamGetSearchArgs (config, where);
-    PXOPT_COPY_STR(config->args, where, "-label", "camRun.label", "==");
+    PXOPT_COPY_S64(config->args, where, "-cam_id",    "camRun.cam_id",    "==");
+    PXOPT_COPY_STR(config->args, where, "-label",     "camRun.label",     "==");
+    PXOPT_COPY_STR(config->args, where, "-reduction", "camRun.reduction", "==");
 
     psString query = pxDataGet("camtool_find_processedexp.sql");
@@ -674,4 +680,7 @@
     }
 
+    // order by cam_id so that the postage stamp parser can easliy find the 'latest' astrometry
+    psStringAppend(&query, " ORDER BY cam_id");
+
     // treat limit == 0 as "no limit"
     if (limit) {
@@ -681,7 +690,4 @@
     }
 
-    // order by cam_id so that the postage stamp parser can easliy find the 'latest' astrometry
-    psStringAppend(&query, "\nORDER BY cam_id");
-
     if (!p_psDBRunQuery(config->dbh, query)) {
         psError(PS_ERR_UNKNOWN, false, "database error");
@@ -720,8 +726,9 @@
 
     psMetadata *where = psMetadataAlloc();
-    PXOPT_COPY_S64(config->args, where, "-cam_id", "camRun.cam_id", "==");
     pxcamGetSearchArgs (config, where);
-    PXOPT_COPY_STR(config->args, where, "-label", "camRun.label", "==");
-    PXOPT_COPY_S16(config->args, where, "-code", "camProcessedExp.fault", "==");
+    PXOPT_COPY_S64(config->args, where, "-cam_id",    "camRun.cam_id",         "==");
+    PXOPT_COPY_STR(config->args, where, "-label",     "camRun.label",          "==");
+    PXOPT_COPY_STR(config->args, where, "-reduction", "camRun.reduction",      "==");
+    PXOPT_COPY_S16(config->args, where, "-code",      "camProcessedExp.fault", "==");
 
     if (!psListLength(where->list) && !psMetadataLookupBool(NULL, config->args, "-all")) {
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/src/camtoolConfig.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/src/camtoolConfig.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/src/camtoolConfig.c	(revision 23594)
@@ -52,5 +52,6 @@
     psMetadata *definebyqueryArgs = psMetadataAlloc();
     pxcamSetSearchArgs(definebyqueryArgs);
-    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-label", 0, "search for label", NULL);
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-label",              0, "search by chipRun label", NULL);
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-reduction",          0, "search by chipRun reduction class", NULL);
 
     psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-set_workdir",        0, "define workdir", NULL);
@@ -68,18 +69,19 @@
     // XXX need to allow multiple exp_ids
     psMetadata *updaterunArgs = psMetadataAlloc();
+    pxcamSetSearchArgs(updaterunArgs);
     psMetadataAddS64(updaterunArgs, PS_LIST_TAIL, "-cam_id",             0, "search by cam_id", 0);
-    pxcamSetSearchArgs(updaterunArgs);
-    psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-label", 0, "search for label", NULL);
-    psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-state", 0, "search for state", NULL);
-
+    psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-label", 		 0, "search by camRun label", NULL);
+    psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-state", 		 0, "search by camRun state", NULL);
+    psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-reduction",          0, "search by camRun reduction class", NULL);
     psMetadataAddBool(updaterunArgs, PS_LIST_TAIL, "-all",               0, "allow everything to be queued without search terms", false);
-    psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-set_state",              0, "set state", NULL);
-    psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-set_label",              0, "set label", NULL);
+    psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-set_state",          0, "set state", NULL);
+    psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-set_label",          0, "set label", NULL);
 
     // -pendingexp
     psMetadata *pendingexpArgs = psMetadataAlloc();
-    psMetadataAddS64(pendingexpArgs, PS_LIST_TAIL, "-cam_id",            0, "search by camtool ID", 0);
     pxcamSetSearchArgs(pendingexpArgs);
-    psMetadataAddStr(pendingexpArgs, PS_LIST_TAIL, "-label", 0, "search for label", NULL);
+    psMetadataAddS64(pendingexpArgs, PS_LIST_TAIL, "-cam_id",            0, "search by cam_id", 0);
+    psMetadataAddStr(pendingexpArgs, PS_LIST_TAIL, "-label",             0, "search by camRun label", NULL);
+    psMetadataAddStr(pendingexpArgs, PS_LIST_TAIL, "-reduction",         0, "search by camRun reduction class", NULL);
     psMetadataAddU64(pendingexpArgs, PS_LIST_TAIL, "-limit",             0, "limit result set to N items", 0);
     psMetadataAddBool(pendingexpArgs, PS_LIST_TAIL, "-simple",           0, "use the simple output format", false);
@@ -87,10 +89,12 @@
     // -pendingimfile
     psMetadata *pendingimfileArgs = psMetadataAlloc();
-    psMetadataAddS64(pendingimfileArgs, PS_LIST_TAIL, "-cam_id", 0,            "search by camtool ID", 0);
     pxcamSetSearchArgs(pendingimfileArgs);
-    psMetadataAddStr(pendingimfileArgs, PS_LIST_TAIL, "-label", 0, "search for label", NULL);
-    psMetadataAddStr(pendingimfileArgs, PS_LIST_TAIL, "-class", 0,            "search by class", NULL);
+    psMetadataAddS64(pendingimfileArgs, PS_LIST_TAIL, "-cam_id",   0,            "search by camtool ID", 0);
+    psMetadataAddStr(pendingimfileArgs, PS_LIST_TAIL, "-label",    0,            "search by camRun label", NULL);
+    psMetadataAddStr(pendingimfileArgs, PS_LIST_TAIL, "-reduction",0,            "search by camRun reduction class", NULL);
     psMetadataAddStr(pendingimfileArgs, PS_LIST_TAIL, "-class_id", 0,            "search by class ID", NULL);
-    psMetadataAddBool(pendingimfileArgs, PS_LIST_TAIL, "-simple", 0,            "use the simple output format", false);
+    psMetadataAddBool(pendingimfileArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+
+    // XXX is this used? psMetadataAddStr(pendingimfileArgs, PS_LIST_TAIL, "-class",    0,            "search by class", NULL);
 
     // -addprocessedexp
@@ -165,7 +169,8 @@
     // -processedexp
     psMetadata *processedexpArgs = psMetadataAlloc();
-    psMetadataAddS64(processedexpArgs, PS_LIST_TAIL, "-cam_id", 0,            "search by camtool ID", 0);
     pxcamSetSearchArgs(processedexpArgs);
-    psMetadataAddStr(processedexpArgs, PS_LIST_TAIL, "-label", 0, "search for label", NULL);
+    psMetadataAddS64(processedexpArgs, PS_LIST_TAIL, "-cam_id",   0,            "search by cam_id", 0);
+    psMetadataAddStr(processedexpArgs, PS_LIST_TAIL, "-label",    0,            "search by camRun label", NULL);
+    psMetadataAddStr(processedexpArgs, PS_LIST_TAIL, "-reduction",0,            "search by camRun reduction class", NULL);
 
     psMetadataAddU64(processedexpArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
@@ -178,10 +183,11 @@
     // XXX need to allow multiple exp_ids
     psMetadata *revertprocessedexpArgs = psMetadataAlloc();
-    psMetadataAddS64(revertprocessedexpArgs, PS_LIST_TAIL, "-cam_id",  0,            "search by cam_id", 0);
     pxcamSetSearchArgs(revertprocessedexpArgs);
-    psMetadataAddStr(revertprocessedexpArgs, PS_LIST_TAIL, "-label", 0, "search for label", NULL);
+    psMetadataAddS64(revertprocessedexpArgs, PS_LIST_TAIL, "-cam_id",   0,            "search by cam_id", 0);
+    psMetadataAddStr(revertprocessedexpArgs, PS_LIST_TAIL, "-label",    0,            "search by camRun label", NULL);
+    psMetadataAddStr(revertprocessedexpArgs, PS_LIST_TAIL, "-reduction",0,            "search by camRun reduction class", NULL);
+    psMetadataAddS16(revertprocessedexpArgs, PS_LIST_TAIL, "-code",     0,            "search by fault code", 0);
 
     psMetadataAddBool(revertprocessedexpArgs, PS_LIST_TAIL, "-all",  0,            "allow everything to be queued without search terms", false);
-    psMetadataAddS16(revertprocessedexpArgs, PS_LIST_TAIL, "-code",  0,            "search by fault code", 0);
 
     // -updateprocessedexp
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/src/chiptool.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/src/chiptool.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/src/chiptool.c	(revision 23594)
@@ -209,5 +209,5 @@
             return false;
         }
-
+        //
         // queue the exp
         if (!pxchipQueueByExpTag(config, exp_id, workdir, label, reduction, expgroup, dvodb, tess_id, end_stage)) {
@@ -415,4 +415,5 @@
     PXOPT_LOOKUP_F32(n_cr, config->args,       	   "-n_cr", false, false);
     PXOPT_LOOKUP_STR(path_base, config->args,  	   "-path_base", false, false);
+    PXOPT_LOOKUP_BOOL(magicked, config->args,  	   "-magicked", false);
 
     // default values
@@ -490,5 +491,5 @@
                                    path_base,
                                    code,
-                                   0    // magic_ds_id
+                                   magicked
             )) {
         // rollback
@@ -540,4 +541,5 @@
     PXOPT_COPY_STR(config->args, where, "-reduction", "chipRun.reduction", "==");
     PXOPT_COPY_STR(config->args, where, "-label", "chipRun.label", "LIKE");
+    PXOPT_COPY_S32(config->args, where, "-magicked", "chipRun.magicked", "==");
 
     psString query = pxDataGet("chiptool_processedimfile.sql");
@@ -1137,6 +1139,6 @@
         }
 
-        // set chipRun.state to 'stop'
-        if (!pxchipRunSetState(config, chipRun->chip_id, "full")) {
+        // set chipRun.state to 'stop' and update the magicked state
+        if (!pxchipRunSetState(config, chipRun->chip_id, "full", chipRun->magicked)) {
             psError(PS_ERR_UNKNOWN, false, "failed to change chipRun.state for chip_id: %" PRId64, chipRun->chip_id);
             psFree(chipRun);
@@ -1209,6 +1211,14 @@
     }
 
+    psString set_magic = "";
+    if (!strcmp(data_state, "full")) {
+        // copy the magicked state from the input to the output when transitioning to full state
+        set_magic = "\n , chipProcessedImfile.magicked = rawImfile.magicked";
+    }
+
     // note only updates if chipRun.state = run_state
-    if (!p_psDBRunQueryF(config->dbh, query, data_state, chip_id, class_id, run_state)) {
+    // XXX note that we have removed this constraint for now
+    if (!p_psDBRunQueryF(config->dbh, query, data_state, set_magic, chip_id, class_id)) {
+        psFree(query);
         psError(PS_ERR_UNKNOWN, false, "database error");
         // rollback
@@ -1227,4 +1237,5 @@
     query = pxDataGet("chiptool_change_exp_state.sql");
     if (!p_psDBRunQueryF(config->dbh, query, data_state, chip_id, data_state)) {
+        psFree(query);
         // rollback
         if (!psDBRollback(config->dbh)) {
@@ -1234,4 +1245,5 @@
         return false;
     }
+    psFree(query);
 
     if (!psDBCommit(config->dbh)) {
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/src/chiptoolConfig.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/src/chiptoolConfig.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/src/chiptoolConfig.c	(revision 23594)
@@ -66,5 +66,5 @@
     pxchipSetSearchArgs (updaterunArgs);
     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, "-state", 0,            "search by state", NULL);
     psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-label",  0,          "search by label", NULL);
     psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-set_state", 0,        "set state", NULL);
@@ -147,4 +147,5 @@
     psMetadataAddStr(addprocessedimfileArgs, PS_LIST_TAIL, "-path_base",  0,            "define base output location", NULL);
     psMetadataAddS16(addprocessedimfileArgs, PS_LIST_TAIL, "-code",  0,            "set fault code", 0);
+    psMetadataAddBool(addprocessedimfileArgs, PS_LIST_TAIL, "-magicked",  0,        "define magicked status", false);
 
     // -processedimfile
@@ -155,4 +156,5 @@
     psMetadataAddStr(processedimfileArgs,  PS_LIST_TAIL, "-reduction",          0, "search by reduction class", NULL);
     psMetadataAddStr(processedimfileArgs,  PS_LIST_TAIL, "-label",              0, "search by chipRun label (LIKE comparison)", NULL);
+    psMetadataAddBool(processedimfileArgs, PS_LIST_TAIL, "-magicked",  0,        "search by magicked status", false);
     pxchipSetSearchArgs(processedimfileArgs);
     psMetadataAddU64(processedimfileArgs, PS_LIST_TAIL, "-limit",  0,           "limit result set to N items", 0);
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/src/difftool.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/src/difftool.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/src/difftool.c	(revision 23594)
@@ -41,4 +41,5 @@
 static bool definepoprunMode(pxConfig *config);
 static bool definebyqueryMode(pxConfig *config);
+static bool definewarpwarpMode(pxConfig *config);
 static bool pendingcleanuprunMode(pxConfig *config);
 static bool pendingcleanupskyfileMode(pxConfig *config);
@@ -48,5 +49,5 @@
 static bool importrunMode(pxConfig *config);
 
-static bool setdiffRunState(pxConfig *config, psS64 diff_id, const char *state);
+static bool setdiffRunState(pxConfig *config, psS64 diff_id, const char *state, bool magicked);
 static bool diffRunComplete(pxConfig *config);
 
@@ -79,4 +80,5 @@
         MODECASE(DIFFTOOL_MODE_DEFINEPOPRUN,          definepoprunMode);
         MODECASE(DIFFTOOL_MODE_DEFINEBYQUERY,         definebyqueryMode);
+        MODECASE(DIFFTOOL_MODE_DEFINEWARPWARP,        definewarpwarpMode);
         MODECASE(DIFFTOOL_MODE_PENDINGCLEANUPRUN,     pendingcleanuprunMode);
         MODECASE(DIFFTOOL_MODE_PENDINGCLEANUPSKYFILE, pendingcleanupskyfileMode);
@@ -131,5 +133,6 @@
             registered,
             tess_id,
-            exp_id
+            exp_id,
+            false
     );
     if (!run) {
@@ -168,5 +171,5 @@
     if (state) {
         // set detRun.state to state
-        return setdiffRunState(config, diff_id, state);
+        return setdiffRunState(config, diff_id, state, false);
     }
 
@@ -282,5 +285,5 @@
 
     if (count == 2) {
-        if (!setdiffRunState(config, diff_id, "new")) {
+        if (!setdiffRunState(config, diff_id, "new", false)) {
             if (!psDBRollback(config->dbh)) {
                 psError(PS_ERR_UNKNOWN, false, "database error");
@@ -506,4 +509,5 @@
     PXOPT_LOOKUP_STR(hostname, config->args, "-hostname", false, false);
     PXOPT_LOOKUP_F32(good_frac, config->args, "-good_frac", false, false);
+    PXOPT_LOOKUP_BOOL(magicked, config->args, "-magicked", false);
 
     if (!psDBTransaction(config->dbh)) {
@@ -537,5 +541,5 @@
                            good_frac,
                            code,
-                           0        // magic_ds_id
+                           magicked
           )) {
         if (!psDBRollback(config->dbh)) {
@@ -730,5 +734,5 @@
 
 
-static bool setdiffRunState(pxConfig *config, psS64 diff_id, const char *state)
+static bool setdiffRunState(pxConfig *config, psS64 diff_id, const char *state, bool magicked)
 {
     PS_ASSERT_PTR_NON_NULL(state, false);
@@ -740,6 +744,7 @@
     }
 
-    char *query = "UPDATE diffRun SET state = '%s' WHERE diff_id = %"PRId64;
-    if (!p_psDBRunQueryF(config->dbh, query, state, diff_id)) {
+    char *query = "UPDATE diffRun SET state = '%s', magicked = %d WHERE diff_id = %"PRId64;
+
+    if (!p_psDBRunQueryF(config->dbh, query, state, magicked, diff_id)) {
         psError(PS_ERR_UNKNOWN, false,
                 "failed to change state for diff_id %"PRId64, diff_id);
@@ -792,5 +797,6 @@
             registered,
             tess_id,
-            exp_id
+            exp_id,
+            false       // magicked
     );
 
@@ -1002,8 +1008,15 @@
     psTrace("difftool", 1, query, warpQuery, diffQuery, expQuery, stackQuery);
 
+    if (!psDBTransaction(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
 
     if (!p_psDBRunQueryF(config->dbh, query, warpQuery, expQuery, diffQuery)) {
         psError(PS_ERR_UNKNOWN, false, "database error");
         psFree(query);
+        if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+        }
         return false;
     }
@@ -1014,12 +1027,17 @@
         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");
-        }
-
+          case PS_ERR_DB_CLIENT:
+            psError(PXTOOLS_ERR_SYS, false, "database error");
+            break;
+          case PS_ERR_DB_SERVER:
+            psError(PXTOOLS_ERR_PROG, false, "database error");
+            break;
+          default:
+            psError(PXTOOLS_ERR_PROG, false, "unknown error");
+            break;
+        }
+        if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+        }
         return false;
     }
@@ -1027,4 +1045,8 @@
         psTrace("difftool", PS_LOG_INFO, "no rows found");
         psFree(output);
+        if (!psDBCommit(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            return false;
+        }
         return true;
     }
@@ -1035,4 +1057,7 @@
         psError(PS_ERR_UNKNOWN, false, "database error");
         psFree(query);
+        if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+        }
         return false;
     }
@@ -1055,4 +1080,7 @@
         if (!mdok) {
             psError(PXTOOLS_ERR_PROG, false, "exp_id not found");
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
             return false;
         }
@@ -1068,4 +1096,7 @@
             psFree(stackQuery);
             psFree(skycell_query);
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
             return false;
         }
@@ -1076,4 +1107,7 @@
             psFree(stackQuery);
             psFree(skycell_query);
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
             return false;
         }
@@ -1084,4 +1118,7 @@
             psFree(stackQuery);
             psFree(skycell_query);
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
             return false;
         }
@@ -1092,4 +1129,7 @@
             psFree(stackQuery);
             psFree(skycell_query);
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
             return false;
         }
@@ -1100,4 +1140,7 @@
             psFree(stackQuery);
             psFree(skycell_query);
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
             return false;
         }
@@ -1107,4 +1150,7 @@
             psFree(stackQuery);
             psFree(skycell_query);
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
             return false;
         }
@@ -1132,5 +1178,6 @@
                 registered,
                 tess_id,
-                exp_id
+                exp_id,
+                false       // magicked
         );
 
@@ -1138,5 +1185,8 @@
             psError(PS_ERR_UNKNOWN, false, "database error");
             psFree(run);
-            return true;
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
+            return false;
         }
         run->diff_id = psDBLastInsertID(config->dbh);
@@ -1149,4 +1199,7 @@
             psFree(skycell_query);
             psFree(query);
+        if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+        }
             return false;
         }
@@ -1160,4 +1213,7 @@
             psFree(skycell_query);
             psFree(query);
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
             return false;
         }
@@ -1165,5 +1221,5 @@
         query = NULL;
 
-        if (!setdiffRunState(config, run->diff_id, "new")) {
+        if (!setdiffRunState(config, run->diff_id, "new", false)) {
             psError(PS_ERR_UNKNOWN, false, "failed to change diffRun.state for diff_id: %" PRId64,
                 run->diff_id);
@@ -1172,4 +1228,7 @@
             psFree(skycell_query);
             psFree(query);
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
             return false;
         }
@@ -1183,4 +1242,10 @@
     psFree(skycell_query);
 
+    if (!psDBCommit(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(list);
+        return false;
+    }
+
     if (!diffRunPrintObjects(stdout, list, !simple)) {
         psError(PS_ERR_UNKNOWN, false, "failed to print object");
@@ -1192,4 +1257,220 @@
     return true;
 }
+
+static bool definewarpwarpMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *selectWhere = psMetadataAlloc();
+    psMetadata *insertWhere = psMetadataAlloc();
+
+    // Restrictions for selecting warps
+    PXOPT_COPY_S64(config->args, selectWhere, "-warp_id", "inputWarpRun.warp_id", "==");
+    PXOPT_COPY_S64(config->args, selectWhere, "-exp_id", "inputRawExp.exp_id", "==");
+    PXOPT_COPY_STR(config->args, selectWhere, "-filter", "inputRawExp.filter", "==");
+    PXOPT_COPY_STR(config->args, selectWhere, "-input_label", "inputWarpRun.label", "==");
+    PXOPT_COPY_STR(config->args, selectWhere, "-template_label", "templateWarpRun.label", "==");
+    PXOPT_COPY_F32(config->args, selectWhere, "-timediff",
+                   "ABS(TIME_TO_SEC(TIMEDIFF(inputRawExp.dateobs, templateRawExp.dateobs)))", "<=");
+    PXOPT_COPY_F32(config->args, selectWhere, "-rotdiff", "ABS(inputRawExp.posang - templateRawExp.posang)", "<=");
+
+    // Restrictions for inserting skycells
+    PXOPT_COPY_F32(config->args, insertWhere, "-good_frac", "inputWarpSkyfile.good_frac", ">=");
+    PXOPT_COPY_F32(config->args, insertWhere, "-good_frac", "templateWarpSkyfile.good_frac", ">=");
+
+    // Additional controls
+    PXOPT_LOOKUP_BOOL(rerun, config->args, "-rerun", false);
+    PXOPT_LOOKUP_BOOL(available, config->args, "-available", false);
+
+    // Settings to apply to defined run
+    PXOPT_LOOKUP_STR(workdir, config->args, "-workdir", true, false); // required options
+    PXOPT_LOOKUP_STR(reduction, config->args, "-reduction", false, false); // option
+    PXOPT_LOOKUP_STR(label, config->args, "-label", false, false); // option
+    PXOPT_LOOKUP_TIME(registered, config->args, "-registered", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+
+    psString select = pxDataGet("difftool_definewarpwarp_select.sql");
+    if (!select) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    psString whereClause = NULL; // The WHERE part of the query
+
+    if (psListLength(selectWhere->list)) {
+        psString new = psDBGenerateWhereConditionSQL(selectWhere, NULL);
+        psStringAppend(&whereClause, "\n%s %s", whereClause ? "AND" : "WHERE", new);
+        psFree(new);
+    }
+    psFree(selectWhere);
+
+    if (!rerun) {
+        psStringAppend(&whereClause, "\n%s diffRun.diff_id IS NULL", whereClause ? "AND" : "WHERE");
+    }
+
+    if (!available) {
+        psStringAppend(&whereClause, "\n%s inputWarpRun.state = 'full'", whereClause ? "AND" : "WHERE");
+    }
+
+    if (!psDBTransaction(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(select);
+        psFree(whereClause);
+        psFree(insertWhere);
+        return false;
+    }
+
+    if (!p_psDBRunQueryF(config->dbh, select,
+                         !rerun ? "\n" : "", // Activate LEFT JOIN against diffRun?
+                         whereClause)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to run query: %s [WITH] %s", select, whereClause);
+        psFree(select);
+        psFree(whereClause);
+        psFree(insertWhere);
+        if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+        }
+        return false;
+    }
+    psFree(select);
+    psFree(whereClause);
+
+    psArray *results = p_psDBFetchResult(config->dbh); // Results of query
+    if (!results) {
+        psErrorCode err = psErrorCodeLast(); // Code for error
+        switch (err) {
+          case PS_ERR_DB_CLIENT:
+            psError(PXTOOLS_ERR_SYS, false, "database error");
+            break;
+          case PS_ERR_DB_SERVER:
+            psError(PXTOOLS_ERR_PROG, false, "database error");
+            break;
+          default:
+            psError(PXTOOLS_ERR_PROG, false, "unknown error");
+            break;
+        }
+        if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+        }
+        psFree(insertWhere);
+        return false;
+    }
+    if (!psArrayLength(results)) {
+        psTrace("difftool", 1, "no rows found");
+        psFree(results);
+        psFree(insertWhere);
+        if (!psDBCommit(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            return false;
+        }
+        return true;
+    }
+
+    psString insert = pxDataGet("difftool_definewarpwarp_insert.sql"); // Insertion for each new run
+
+    if (psListLength(insertWhere->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(insertWhere, NULL);
+        psStringAppend(&insert, "\n%s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(insertWhere);
+
+    psArray *list = psArrayAllocEmpty(16); // List of runs defined, to print
+    long numGood = 0;                   // Number of good rows added
+    for (long i = 0; i < results->n; i++) {
+        psMetadata *row = results->data[i]; // Result row from query
+
+        psS64 exp_id = psMetadataLookupS64(NULL, row, "input_exp_id");
+        psS64 input_id = psMetadataLookupS64(NULL, row, "input_warp_id");
+        psS64 template_id = psMetadataLookupS64(NULL, row, "template_warp_id");
+        const char *tess_id = psMetadataLookupStr(NULL, row, "tess_id");
+        if (!exp_id || !input_id || !template_id || !tess_id) {
+            psError(PXTOOLS_ERR_PROG, false, "Identifiers not found");
+            psFree(list);
+            psFree(insert);
+            psFree(results);
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
+            return false;
+        }
+
+        diffRunRow *run = diffRunRowAlloc(0, "reg", workdir, label, reduction, NULL, registered,
+                                          tess_id, exp_id, false); // Run to insert
+        if (!diffRunInsertObject(config->dbh, run)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            psFree(run);
+            psFree(list);
+            psFree(insert);
+            psFree(results);
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
+            return false;
+        }
+        run->diff_id = psDBLastInsertID(config->dbh); // Difference run identifier
+
+        // Convert identifiers to string, for insertion into query
+        psString diff = NULL, input = NULL, template = NULL; // String versions of identifiers
+        psStringAppend(&diff, "%" PRId64, run->diff_id);
+        psStringAppend(&input, "%" PRId64, input_id);
+        psStringAppend(&template, "%" PRId64, template_id);
+
+        if (!p_psDBRunQueryF(config->dbh, insert, diff, input, template, input, template)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            psFree(input);
+            psFree(template);
+            psFree(diff);
+            psFree(run);
+            psFree(list);
+            psFree(insert);
+            psFree(results);
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
+            return false;
+        }
+        psFree(input);
+        psFree(template);
+        psFree(diff);
+
+        if (!setdiffRunState(config, run->diff_id, "new", false)) {
+            psError(PS_ERR_UNKNOWN, false, "Failed to change diffRun.state for diff_id: %" PRId64,
+                    run->diff_id);
+            psFree(run);
+            psFree(list);
+            psFree(insert);
+            psFree(results);
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
+            return false;
+        }
+
+        psArrayAdd(list, list->n, run);
+        psFree(run);                    // Drop reference
+
+        numGood++;
+    }
+    psFree(insert);
+    psFree(results);
+
+    if (!psDBCommit(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(list);
+        return false;
+    }
+
+    if (!diffRunPrintObjects(stdout, list, !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print object");
+        psFree(list);
+        return false;
+    }
+    psFree(list);
+
+    return true;
+}
+
 
 static bool pendingcleanuprunMode(pxConfig *config)
@@ -1428,7 +1709,8 @@
 
         psS64 diff_id = psMetadataLookupS64(NULL, row, "diff_id");
+        bool magicked = psMetadataLookupBool(NULL, row, "magicked");
 
         // set diffRun.state to 'stop'
-        if (!setdiffRunState(config, diff_id, "full")) {
+        if (!setdiffRunState(config, diff_id, "full", magicked)) {
             psError(PS_ERR_UNKNOWN, false, "failed to change diffRun.state for diff_id: %" PRId64,
                 diff_id);
@@ -1447,5 +1729,5 @@
     char sqlFilename[80];
   } ExportTable;
-  
+
   int numExportFiles = 3;
 
@@ -1526,11 +1808,11 @@
 {
   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);
 
@@ -1557,5 +1839,5 @@
     psAssert (item, "entry not in input?");
     psAssert (item->type == PS_DATA_METADATA_MULTI, "entry not multi?");
-  
+
     switch (i) {
       case 0:
@@ -1571,5 +1853,5 @@
         }
         break;
-        
+
       case 1:
         for (int i = 0; i < item->data.list->n; i++) {
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/src/difftool.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/src/difftool.h	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/src/difftool.h	(revision 23594)
@@ -35,4 +35,5 @@
     DIFFTOOL_MODE_DEFINEPOPRUN,
     DIFFTOOL_MODE_DEFINEBYQUERY,
+    DIFFTOOL_MODE_DEFINEWARPWARP,
     DIFFTOOL_MODE_PENDINGCLEANUPRUN,
     DIFFTOOL_MODE_PENDINGCLEANUPSKYFILE,
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/src/difftoolConfig.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/src/difftoolConfig.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/src/difftoolConfig.c	(revision 23594)
@@ -107,7 +107,8 @@
     psMetadataAddF32(adddiffskyfileArgs, PS_LIST_TAIL, "-kernel_xy",  0, "define kernel xy moment", NAN);
     psMetadataAddF32(adddiffskyfileArgs, PS_LIST_TAIL, "-kernel_yy",  0, "define kernel yy moment", NAN);
-    psMetadataAddS32(adddiffskyfileArgs, PS_LIST_TAIL, "-sources",  0,            "define number of sources", 0);
-    psMetadataAddStr(adddiffskyfileArgs, PS_LIST_TAIL, "-hostname", 0,            "define hostname", 0);
-    psMetadataAddF32(adddiffskyfileArgs, PS_LIST_TAIL, "-good_frac",  0,            "define %% of good pixels", NAN);
+    psMetadataAddS32(adddiffskyfileArgs, PS_LIST_TAIL, "-sources",  0,   "define number of sources", 0);
+    psMetadataAddStr(adddiffskyfileArgs, PS_LIST_TAIL, "-hostname", 0,   "define hostname", 0);
+    psMetadataAddF32(adddiffskyfileArgs, PS_LIST_TAIL, "-good_frac",  0, "define %% of good pixels", NAN);
+    psMetadataAddBool(adddiffskyfileArgs, PS_LIST_TAIL, "-magicked",  0, "define magicked state", false);
 
     // -diffskyfile
@@ -164,4 +165,22 @@
     psMetadataAddBool(definebyqueryArgs, PS_LIST_TAIL, "-simple", 0, "use the simple output format", false);
 
+    // -definewarpwarp
+    psMetadata *definewarpwarpArgs = psMetadataAlloc();
+    psMetadataAddS64(definewarpwarpArgs, PS_LIST_TAIL, "-warp_id", 0, "search by warp ID", 0);
+    psMetadataAddS64(definewarpwarpArgs, PS_LIST_TAIL, "-exp_id", 0, "search by exposure ID", 0);
+    psMetadataAddStr(definewarpwarpArgs, PS_LIST_TAIL, "-filter", 0, "search by filter", NULL);
+    psMetadataAddF32(definewarpwarpArgs, PS_LIST_TAIL, "-timediff", 0, "limit time difference between input and template", NAN);
+    psMetadataAddF32(definewarpwarpArgs, PS_LIST_TAIL, "-rotdiff", 0, "limit rotator difference between input and template", NAN);
+    psMetadataAddStr(definewarpwarpArgs, PS_LIST_TAIL, "-input_label", 0, "search by warp label for input", NULL);
+    psMetadataAddStr(definewarpwarpArgs, PS_LIST_TAIL, "-template_label", 0, "search by warp label for template", NULL);
+    psMetadataAddF32(definewarpwarpArgs, PS_LIST_TAIL, "-good_frac", 0, "minimum good fraction of skycell", NAN);
+    psMetadataAddStr(definewarpwarpArgs, PS_LIST_TAIL, "-workdir", 0, "define workdir (required)", NULL);
+    psMetadataAddStr(definewarpwarpArgs, PS_LIST_TAIL, "-label",  0, "define label", NULL);
+    psMetadataAddStr(definewarpwarpArgs, PS_LIST_TAIL, "-reduction",  0, "define reduction class", NULL);
+    psMetadataAddTime(definewarpwarpArgs, PS_LIST_TAIL, "-registered", 0, "time detrend run was registered", now);
+    psMetadataAddBool(definewarpwarpArgs, PS_LIST_TAIL, "-rerun", 0, "define new run even if one exists", false);
+    psMetadataAddBool(definewarpwarpArgs, PS_LIST_TAIL, "-available", 0, "define new run even if warpRun has some faults", false);
+    psMetadataAddBool(definewarpwarpArgs, PS_LIST_TAIL, "-simple", 0, "use the simple output format", false);
+
     // -pendingcleanuprun
     psMetadata *pendingcleanuprunArgs = psMetadataAlloc();
@@ -214,4 +233,5 @@
     PXOPT_ADD_MODE("-definepoprun",     "", DIFFTOOL_MODE_DEFINEPOPRUN,      definepoprunArgs);
     PXOPT_ADD_MODE("-definebyquery",    "", DIFFTOOL_MODE_DEFINEBYQUERY,     definebyqueryArgs);
+    PXOPT_ADD_MODE("-definewarpwarp",   "", DIFFTOOL_MODE_DEFINEWARPWARP,    definewarpwarpArgs);
     PXOPT_ADD_MODE("-pendingcleanuprun",     "show runs that need to be cleaned up", DIFFTOOL_MODE_PENDINGCLEANUPRUN,    pendingcleanuprunArgs);
     PXOPT_ADD_MODE("-pendingcleanupskyfile", "show runs that need to be cleaned up", DIFFTOOL_MODE_PENDINGCLEANUPSKYFILE, pendingcleanupskyfileArgs);
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/src/disttool.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/src/disttool.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/src/disttool.c	(revision 23594)
@@ -98,12 +98,7 @@
     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
+    // XXX: all of the following concerns will be managed properly by definebyquery
+
     // 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 
@@ -113,5 +108,5 @@
     // change between the time that it is queued.
 
-    if (!distRunInsert(config->dbh, 0, stage, stage_id, chip_id, set_label, outroot, clean, "new", 0)) {
+    if (!distRunInsert(config->dbh, 0, stage, stage_id, set_label, outroot, clean, "new", NULL, 0)) {
         psError(PS_ERR_UNKNOWN, false, "database error");
         return false;
@@ -151,16 +146,16 @@
     }
 
-    psString query = psStringCopy("UPDATE distRun");
+    psString query = psStringCopy("UPDATE distRun SET time_stamp = UTC_TIMESTAMP()");
 
     if (state) {
-        psStringAppend(&query, " SET state = '%s'", state);
+        psStringAppend(&query, " , state = '%s'", state);
     }
 
     if (label) {
-        psStringAppend(&query, " SET label = '%s'", label);
+        psStringAppend(&query, " , label = '%s'", label);
     }
 
     if (code) {
-        psStringAppend(&query, " SET fault = %d", code);
+        psStringAppend(&query, " , fault = %d", code);
     }
 
@@ -301,4 +296,5 @@
     PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
     PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+    PXOPT_LOOKUP_BOOL(need_magic, config->args, "-need_magic", false);
 
     // look for "inputs" that need to processed
@@ -323,5 +319,33 @@
     }
 
-    if (!p_psDBRunQuery(config->dbh, query)) {
+    psString    chip_magic = "";
+    psString    raw_where = "";
+    psString    chip_where = "";
+    psString    camera_where = "";
+    psString    fake_where = "";
+    psString    warp_where = "";
+    psString    diff_where = "";
+    psString    stack_where = "";
+
+    if (need_magic) {
+        chip_magic = psStringCopy("\nAND chipRun.magicked");
+        raw_where  = psStringCopy("\nAND rawExp.magicked");
+        chip_where = psStringCopy("\nAND (distRun.clean OR chipRun.magicked)");
+        // chipRun must be magicked to allow release camera level masks
+        camera_where = psStringCopy("\nAND (distRun.clean OR chipRun.magicked)");
+        warp_where = psStringCopy("\nAND (distRun.clean OR warpRun.magicked)");
+        diff_where = psStringCopy("\nAND (distRun.clean OR diffRun.magicked)");
+    }
+
+    if (!p_psDBRunQueryF(config->dbh,
+            query,
+            chip_magic,
+            raw_where,
+            chip_where,
+            camera_where,
+            fake_where,
+            warp_where,
+            diff_where,
+            stack_where)) {
         psError(PS_ERR_UNKNOWN, false, "database error");
         psFree(query);
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/src/disttoolConfig.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/src/disttoolConfig.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/src/disttoolConfig.c	(revision 23594)
@@ -47,5 +47,4 @@
     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);
@@ -76,4 +75,5 @@
     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);
+    psMetadataAddBool(pendingcomponentArgs, PS_LIST_TAIL, "-need_magic", 0, "magic is needed", false);
     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);
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/src/magicdstool.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/src/magicdstool.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/src/magicdstool.c	(revision 23594)
@@ -40,5 +40,5 @@
 
 static bool setmagicDSRunState(pxConfig *config, psS64 magic_id, const char *state);
-static bool magicDSRunComplete(pxConfig *config);
+static bool magicDSRunComplete(pxConfig *config, bool setmagicked);
 static bool magicDSGetIDs(pxConfig *config, psString stage, psS64 magic_id, psS64 *stage_id, psS64 *cam_id);
 
@@ -242,5 +242,5 @@
 
         // create a new magicRun for this group
-        magicRunRow *run = magicRunRowAlloc(0, exp_id, "run", workdir, "dirty", label, dvodb, registered, 0);
+        magicRunRow *run = magicRunRowAlloc(0, exp_id, "new", workdir, "dirty", label, dvodb, registered, 0);
         if (!run) {
             psAbort("failed to alloc magicRun object");
@@ -320,10 +320,11 @@
     PXOPT_LOOKUP_STR(stage, config->args, "-stage", true, false);
     PXOPT_LOOKUP_STR(outroot, config->args, "-outroot", true, false);
+
+    // optional
     PXOPT_LOOKUP_STR(recoveryroot, config->args, "-recoveryroot", false, false);
     PXOPT_LOOKUP_BOOL(re_place, config->args, "-replace", false);
     PXOPT_LOOKUP_BOOL(remove, config->args, "-remove", false);
-
-    // optional
     PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+    PXOPT_LOOKUP_STR(label, config->args, "-label", false, false);
 
     psS64 stage_id = 0, cam_id = 0;
@@ -337,8 +338,9 @@
             0,          // ID
             magic_id,
-            "run",      // state
+            "new",      // state
             stage,
             stage_id,
             cam_id,
+            label,
             outroot,
             recoveryroot,
@@ -396,5 +398,5 @@
     PXOPT_COPY_S64(config->args, where, "-magic_ds_id", "magic_ds_id", "==");
     PXOPT_COPY_S64(config->args, where, "-magic_id", "magic_id", "==");
-//    PXOPT_COPY_S64(config->args, where, "-exp_id", "exp_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-label", "label", "==");
 
     PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
@@ -526,4 +528,66 @@
 }
 
+static bool
+setRunMagicked(pxConfig *config, psS64 magic_ds_id)
+{
+    // first query the magicDSRun to find the stage and the stage_id
+    psString query = "SELECT stage, stage_id from magicDSRun where magic_ds_id = %" PRId64;
+
+    if (!p_psDBRunQueryF(config->dbh, query, magic_ds_id)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psError(PS_ERR_UNKNOWN, true, "magicDSRun not found for magic_ds_id %" PRId64, magic_ds_id);
+        psFree(output);
+        return false;
+    }
+    if (psArrayLength(output) > 1) {
+        psError(PS_ERR_UNKNOWN, true, "unexpected number of rows found %ld for magic_ds_id %" PRId64,
+            psArrayLength(output), magic_ds_id);
+        psFree(output);
+        return false;
+    }
+    psMetadata *row = output->data[0];
+
+    psString stage = psMetadataLookupStr(NULL, row, "stage");
+    psS64 stage_id = psMetadataLookupS64(NULL, row, "stage_id");
+
+
+    // chose the appropriate query based on the stage
+    if (!strcmp(stage, "raw")) {
+        query = "UPDATE rawExp SET magicked = 1 where exp_id = %" PRId64;
+    } else if (!strcmp(stage, "chip")) {
+        query = "UPDATE chipRun SET magicked = 1 where chip_id = %" PRId64;
+    } else if (!strcmp(stage, "warp")) {
+        query = "UPDATE warpRun SET magicked = 1 where warp_id = %" PRId64;
+    } else if (!strcmp(stage, "diff")) {
+        query = "UPDATE diffRun SET magicked = 1 where diff_id = %" PRId64;
+    } else {
+        psError(PS_ERR_UNKNOWN, true, "unexpected value for stage: %s found", stage);
+        psFree(output);
+        return false;
+    }
+    if (!p_psDBRunQueryF(config->dbh, query, stage_id)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    psFree(output);
+
+    psU64 affected = psDBAffectedRows(config->dbh);
+    if (affected != 1) {
+        psError(PS_ERR_UNKNOWN, false, "should have affected 1 row");
+        return false;
+    }
+
+    return true;
+}
+
 static bool adddestreakedfileMode(pxConfig *config)
 {
@@ -551,6 +615,10 @@
 
     if (setmagicked) {
+        // set the image file's magicked flag
         if (!setMagicked(config, magic_ds_id, component)) {
             psError(PS_ERR_UNKNOWN, false, "setMagicked failed");
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
             return false;
         }
@@ -566,5 +634,5 @@
     }
 
-    if (!magicDSRunComplete(config)) {
+    if (!magicDSRunComplete(config, setmagicked)) {
             // rollback
         if (!psDBRollback(config->dbh)) {
@@ -591,5 +659,5 @@
 
     if (!strcmp(stage, "diff")) {
-        // don't need these ids for diff stage
+        // don't need these ids for diff stage because diff_id is in the magicRun
         *stage_id = 0;
         *cam_id = 0;
@@ -652,5 +720,5 @@
 }
 
-static bool magicDSRunComplete(pxConfig *config)
+static bool magicDSRunComplete(pxConfig *config, bool setmagicked)
 {
     PS_ASSERT_PTR_NON_NULL(config, false);
@@ -685,6 +753,13 @@
         psS64 magic_ds_id = psMetadataLookupS64(NULL, row, "magic_ds_id");
 
-        // set magicDSRun.state to 'stop'
-        if (!setmagicDSRunState(config, magic_ds_id, "stop")) {
+        // if requested, set stageRun.magicked
+        if (setmagicked && !setRunMagicked(config, magic_ds_id)) {
+            psError(PS_ERR_UNKNOWN, false, "failed to change stageRun.magicked for magic_ds_id: %" PRId64,
+                magic_ds_id);
+            return false;
+        }
+
+        // set magicDSRun.state to 'full'
+        if (!setmagicDSRunState(config, magic_ds_id, "full")) {
             psError(PS_ERR_UNKNOWN, false, "failed to change magicDSRun.state for magic_ds_id: %" PRId64,
                 magic_ds_id);
@@ -795,6 +870,6 @@
     // check that state is a valid string value
     if (!(
-            (strncmp(state, "run", 4) == 0)
-            || (strncmp(state, "stop", 5) == 0)
+            (strncmp(state, "new", 4) == 0)
+            || (strncmp(state, "full", 5) == 0)
         )
     ) {
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/src/magicdstoolConfig.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/src/magicdstoolConfig.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/src/magicdstoolConfig.c	(revision 23594)
@@ -70,5 +70,5 @@
     psMetadataAddBool(definerunArgs, PS_LIST_TAIL, "-replace", 0, "use the simple output format", false);
     psMetadataAddBool(definerunArgs, PS_LIST_TAIL, "-remove", 0, "use the simple output format", false);
-//    psMetadataAddStr(definerunArgs, PS_LIST_TAIL, "-label", 0, "define label", NULL);
+    psMetadataAddStr(definerunArgs, PS_LIST_TAIL, "-label", 0, "define label", NULL);
     psMetadataAddBool(definerunArgs, PS_LIST_TAIL, "-simple", 0, "use the simple output format", false);
 
@@ -88,5 +88,5 @@
     psMetadataAddS64(todestreakArgs, PS_LIST_TAIL, "-magic_ds_id", 0, "search by magic Destreak ID", 0);
     psMetadataAddS64(todestreakArgs, PS_LIST_TAIL, "-magic_id", 0, "search by magic ID", 0);
-//    psMetadataAddS64(todestreakArgs, PS_LIST_TAIL, "-exp_id", 0, "search by exposure ID", 0);
+    psMetadataAddStr(todestreakArgs, PS_LIST_TAIL, "-label", 0, "define label", NULL);
     psMetadataAddU64(todestreakArgs, PS_LIST_TAIL, "-limit", 0, "limit result set to N items", 0);
     psMetadataAddBool(todestreakArgs, PS_LIST_TAIL, "-simple", 0, "use the simple output format", false);
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/src/magictool.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/src/magictool.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/src/magictool.c	(revision 23594)
@@ -217,5 +217,5 @@
 
         // create a new magicRun for this group
-        magicRunRow *run = magicRunRowAlloc(0, exp_id, diff_id, "run", workdir, "dirty", label, dvodb, registered, 0);
+        magicRunRow *run = magicRunRowAlloc(0, exp_id, diff_id, "new", workdir, "dirty", label, dvodb, registered, 0);
         if (!run) {
             psAbort("failed to alloc magicRun object");
@@ -538,5 +538,5 @@
 
     if (code > 0) {
-        char *query = "UPDATE magicRun SET fault = %d, state = 'stop' WHERE magic_id = %" PRId64;
+        char *query = "UPDATE magicRun SET fault = %d, state = 'full' WHERE magic_id = %" PRId64;
         if (!p_psDBRunQueryF(config->dbh, query, code, magic_id)) {
             psError(PS_ERR_UNKNOWN, false,
@@ -1088,8 +1088,8 @@
     }
 
-    // Set to "run"
+    // Set to "new"
     {
         psString query = psStringCopy("UPDATE magicRun JOIN magicMask USING(magic_id) "
-                                      "SET magicRun.state = 'run' WHERE magicMask.fault != 0");
+                                      "SET magicRun.state = 'new' WHERE magicMask.fault != 0");
 
         if (psListLength(where->list)) {
@@ -1481,6 +1481,6 @@
     // check that state is a valid string value
     if (!(
-            (strncmp(state, "run", 4) == 0)
-            || (strncmp(state, "stop", 5) == 0)
+            (strncmp(state, "new", 4) == 0)
+            || (strncmp(state, "full", 5) == 0)
             || (strncmp(state, "reg", 4) == 0)
         )
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/src/pxcam.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/src/pxcam.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/src/pxcam.c	(revision 23594)
@@ -42,5 +42,4 @@
     psMetadataAddStr(md,  PS_LIST_TAIL, "-comment",            0, "search by comment", NULL);
     psMetadataAddStr(md,  PS_LIST_TAIL, "-filelevel",          0, "search by filelevel", NULL);
-    psMetadataAddStr(md,  PS_LIST_TAIL, "-reduction",          0, "search by reduction class", NULL);
     psMetadataAddStr(md,  PS_LIST_TAIL, "-filter",             0, "search for filter", NULL);
     psMetadataAddF64(md,  PS_LIST_TAIL, "-airmass_min",        0, "define min airmass", NAN);
@@ -77,44 +76,43 @@
 bool pxcamGetSearchArgs (pxConfig *config, psMetadata *where) {
 
-    PXOPT_COPY_S64(config->args, where, "-chip_id", "chip_id", "==");
-    PXOPT_COPY_S64(config->args, where, "-exp_id", "rawExp.exp_id", "==");
-    PXOPT_COPY_STR(config->args, where, "-exp_name", "exp_name", "==");
-    PXOPT_COPY_STR(config->args, where, "-inst", "camera", "==");
-    PXOPT_COPY_STR(config->args, where, "-telescope", "telescope", "==");
-    PXOPT_COPY_TIME(config->args, where, "-dateobs_begin", "dateobs", ">=");
-    PXOPT_COPY_TIME(config->args, where, "-dateobs_end", "dateobs", "<=");
-    PXOPT_COPY_STR(config->args, where, "-exp_tag", "exp_tag", "==");
-    PXOPT_COPY_STR(config->args, where, "-exp_type", "exp_type", "==");
-    PXOPT_COPY_STR(config->args, where, "-comment", "rawExp.comment", "LIKE");
-    PXOPT_COPY_STR(config->args, where, "-filelevel", "filelevel", "==");
-    PXOPT_COPY_STR(config->args, where, "-reduction", "reduction", "==");
-    PXOPT_COPY_STR(config->args, where, "-filter", "filter", "==");
-    PXOPT_COPY_F64(config->args, where, "-airmass_min", "airmass", ">=");
-    PXOPT_COPY_F64(config->args, where, "-airmass_max", "airmass", "<");
-    PXOPT_COPY_F64(config->args, where, "-ra_min", "ra", ">=");
-    PXOPT_COPY_F64(config->args, where, "-ra_max", "ra", "<");
-    PXOPT_COPY_F64(config->args, where, "-decl_min", "decl", ">=");
-    PXOPT_COPY_F64(config->args, where, "-decl_max", "decl", "<");
-    PXOPT_COPY_F32(config->args, where, "-exp_time_min", "exp_time", ">=");
-    PXOPT_COPY_F32(config->args, where, "-exp_time_max", "exp_time", "<");
-    PXOPT_COPY_F32(config->args, where, "-sat_pixel_frac_min", "sat_pixel_frac", ">=");
-    PXOPT_COPY_F32(config->args, where, "-sat_pixel_frac_max", "sat_pixel_frac", "<");
-    PXOPT_COPY_F64(config->args, where, "-bg_min", "bt", ">=");
-    PXOPT_COPY_F64(config->args, where, "-bg_max", "bt", "<");
-    PXOPT_COPY_F64(config->args, where, "-bg_stdev_min", "bg_stdev", ">=");
-    PXOPT_COPY_F64(config->args, where, "-bg_stdev_max", "bg_stdev", "<");
-    PXOPT_COPY_F64(config->args, where, "-bg_mean_stdev_min", "bg_mean_stdev", ">=");
-    PXOPT_COPY_F64(config->args, where, "-bg_mean_stdev_max", "bg_mean_stdev", "<");
-    PXOPT_COPY_F64(config->args, where, "-alt_min", "alt", ">=");
-    PXOPT_COPY_F64(config->args, where, "-alt_max", "alt", "<");
-    PXOPT_COPY_F64(config->args, where, "-az_min", "az", ">=");
-    PXOPT_COPY_F64(config->args, where, "-az_max", "az", "<");
-    PXOPT_COPY_F32(config->args, where, "-ccd_temp_min", "ccd_temp", ">=");
-    PXOPT_COPY_F32(config->args, where, "-ccd_temp_max", "ccd_temp", "<");
-    PXOPT_COPY_F64(config->args, where, "-posang_min", "posang", ">=");
-    PXOPT_COPY_F64(config->args, where, "-posang_max", "posang", "<");
-    PXOPT_COPY_STR(config->args, where, "-object", "object", "==");
-    PXOPT_COPY_F32(config->args, where, "-solang_min", "solang", ">=");
-    PXOPT_COPY_F32(config->args, where, "-solang_max", "solang", "<");
+    PXOPT_COPY_S64(config->args,  where, "-chip_id",            "chipRun.chip_id", 	 "==");
+    PXOPT_COPY_S64(config->args,  where, "-exp_id",             "rawExp.exp_id",   	 "==");
+    PXOPT_COPY_STR(config->args,  where, "-exp_name",           "rawExp.exp_name", 	 "==");
+    PXOPT_COPY_STR(config->args,  where, "-inst",               "rawExp.camera",   	 "==");
+    PXOPT_COPY_STR(config->args,  where, "-telescope",          "rawExp.telescope",	 "==");
+    PXOPT_COPY_TIME(config->args, where, "-dateobs_begin",      "rawExp.dateobs",  	 ">=");
+    PXOPT_COPY_TIME(config->args, where, "-dateobs_end",        "rawExp.dateobs",  	 "<=");
+    PXOPT_COPY_STR(config->args,  where, "-exp_tag",            "rawExp.exp_tag",  	 "==");
+    PXOPT_COPY_STR(config->args,  where, "-exp_type",           "rawExp.exp_type", 	 "==");
+    PXOPT_COPY_STR(config->args,  where, "-comment",            "rawExp.comment",  	 "LIKE");
+    PXOPT_COPY_STR(config->args,  where, "-filelevel",          "rawExp.filelevel",	 "==");
+    PXOPT_COPY_STR(config->args,  where, "-filter",             "rawExp.filter",         "==");
+    PXOPT_COPY_F64(config->args,  where, "-airmass_min",        "rawExp.airmass",        ">=");
+    PXOPT_COPY_F64(config->args,  where, "-airmass_max",        "rawExp.airmass",        "<");
+    PXOPT_COPY_F64(config->args,  where, "-ra_min",             "rawExp.ra",             ">=");
+    PXOPT_COPY_F64(config->args,  where, "-ra_max",             "rawExp.ra",             "<");
+    PXOPT_COPY_F64(config->args,  where, "-decl_min",           "rawExp.decl",           ">=");
+    PXOPT_COPY_F64(config->args,  where, "-decl_max",           "rawExp.decl",           "<");
+    PXOPT_COPY_F32(config->args,  where, "-exp_time_min",       "rawExp.exp_time",       ">=");
+    PXOPT_COPY_F32(config->args,  where, "-exp_time_max",       "rawExp.exp_time",       "<");
+    PXOPT_COPY_F32(config->args,  where, "-sat_pixel_frac_min", "rawExp.sat_pixel_frac", ">=");
+    PXOPT_COPY_F32(config->args,  where, "-sat_pixel_frac_max", "rawExp.sat_pixel_frac", "<");
+    PXOPT_COPY_F64(config->args,  where, "-bg_min",             "rawExp.bg",             ">=");
+    PXOPT_COPY_F64(config->args,  where, "-bg_max",             "rawExp.bg",             "<");
+    PXOPT_COPY_F64(config->args,  where, "-bg_stdev_min",       "rawExp.bg_stdev",       ">=");
+    PXOPT_COPY_F64(config->args,  where, "-bg_stdev_max",       "rawExp.bg_stdev",       "<");
+    PXOPT_COPY_F64(config->args,  where, "-bg_mean_stdev_min",  "rawExp.bg_mean_stdev",  ">=");
+    PXOPT_COPY_F64(config->args,  where, "-bg_mean_stdev_max",  "rawExp.bg_mean_stdev",  "<");
+    PXOPT_COPY_F64(config->args,  where, "-alt_min",            "rawExp.alt",            ">=");
+    PXOPT_COPY_F64(config->args,  where, "-alt_max",            "rawExp.alt",            "<");
+    PXOPT_COPY_F64(config->args,  where, "-az_min",             "rawExp.az",             ">=");
+    PXOPT_COPY_F64(config->args,  where, "-az_max",             "rawExp.az",             "<");
+    PXOPT_COPY_F32(config->args,  where, "-ccd_temp_min",       "rawExp.ccd_temp",       ">=");
+    PXOPT_COPY_F32(config->args,  where, "-ccd_temp_max",       "rawExp.ccd_temp",       "<");
+    PXOPT_COPY_F64(config->args,  where, "-posang_min",         "rawExp.posang",         ">=");
+    PXOPT_COPY_F64(config->args,  where, "-posang_max",         "rawExp.posang",         "<");
+    PXOPT_COPY_STR(config->args,  where, "-object",             "rawExp.object",         "==");
+    PXOPT_COPY_F32(config->args,  where, "-solang_min",         "rawExp.solang",         ">=");
+    PXOPT_COPY_F32(config->args,  where, "-solang_max",         "rawExp.solang",         "<");
 
     return true;
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/src/pxchip.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/src/pxchip.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/src/pxchip.c	(revision 23594)
@@ -31,8 +31,10 @@
 bool pxchipSetSearchArgs (psMetadata *md) {
 
+    // psMetadataAddStr(md,  PS_LIST_TAIL, "-class_id",           0, "search by class ID", NULL);
+    // psMetadataAddStr(md,  PS_LIST_TAIL, "-reduction",          0, "search by reduction class", NULL);
+
     // XXX need to allow multiple exp_ids
     psMetadataAddS64(md,  PS_LIST_TAIL, "-exp_id",             0, "search by exp_id", 0);
     psMetadataAddStr(md,  PS_LIST_TAIL, "-exp_name",           0, "search by exp_name", NULL);
-    // psMetadataAddStr(md,  PS_LIST_TAIL, "-class_id",           0, "search by class ID", NULL);
     psMetadataAddStr(md,  PS_LIST_TAIL, "-inst",               0, "search for camera", NULL);
     psMetadataAddStr(md,  PS_LIST_TAIL, "-telescope",          0, "search for telescope", NULL);
@@ -42,5 +44,4 @@
     psMetadataAddStr(md,  PS_LIST_TAIL, "-exp_type",           0, "search by exp_type", NULL);
     psMetadataAddStr(md,  PS_LIST_TAIL, "-filelevel",          0, "search by filelevel", NULL);
-    // psMetadataAddStr(md,  PS_LIST_TAIL, "-reduction",          0, "search by reduction class", NULL);
     psMetadataAddStr(md,  PS_LIST_TAIL, "-filter",             0, "search for filter", NULL);
     psMetadataAddF32(md,  PS_LIST_TAIL, "-airmass_min",        0, "search by min airmass", NAN);
@@ -128,5 +129,5 @@
 }
 
-bool pxchipRunSetState(pxConfig *config, psS64 chip_id, const char *state)
+bool pxchipRunSetState(pxConfig *config, psS64 chip_id, const char *state, const bool magicked)
 {
     PS_ASSERT_PTR_NON_NULL(config, false);
@@ -139,6 +140,6 @@
     }
 
-    char *query = "UPDATE chipRun SET state = '%s' WHERE chip_id = %" PRId64;
-    if (!p_psDBRunQueryF(config->dbh, query, state, chip_id)) {
+    char *query = "UPDATE chipRun SET state = '%s', magicked = %d WHERE chip_id = %" PRId64;
+    if (!p_psDBRunQueryF(config->dbh, query, state, magicked, chip_id)) {
         psError(PS_ERR_UNKNOWN, false,
                 "failed to change state for chip_id %" PRId64, chip_id);
@@ -158,4 +159,14 @@
     if (!pxIsValidState(state)) {
         psError(PS_ERR_UNKNOWN, false, "invalid chipRun state: %s", state);
+        return false;
+    }
+
+    if (!strcmp(state, "full")) {
+        // There are states that need to be met for a run to be set to full that we don't
+        // check here.
+        // for example all of the run's Imfiles must have chipProcessedImfile.data_state == "full"
+        // chipRun.magicked = (SUM(!chipProcessedImfile.magicked) = 0)
+        // so don't do allow setting the state to full
+        psError(PS_ERR_UNKNOWN, true, "cannot use -updaterun so set chipRun state to full");
         return false;
     }
@@ -253,5 +264,7 @@
             dvodb,
             tess_id,
-            end_stage)
+            end_stage, 
+            0           // magicked
+            )
     ) {
         if (!psDBRollback(config->dbh)) {
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/src/pxchip.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/src/pxchip.h	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/src/pxchip.h	(revision 23594)
@@ -25,5 +25,5 @@
 #include "pxtools.h"
 
-bool pxchipRunSetState(pxConfig *config, psS64 chip_id, const char *state);
+bool pxchipRunSetState(pxConfig *config, psS64 chip_id, const char *state, const bool magicked);
 bool pxchipRunSetStateByQuery(pxConfig *config, psMetadata *where, const char *state);
 bool pxchipRunSetLabel(pxConfig *config, psS64 chip_id, const char *label);
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/src/pxerrors.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/src/pxerrors.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/src/pxerrors.c	(revision 23594)
@@ -24,5 +24,5 @@
 #include "pxtools.h"
 
-psExit pxerrorGetExitStatus () {
+psExit pxerrorGetExitStatus(void) {
 
     psErrorCode err = psErrorCodeLast ();
@@ -38,5 +38,5 @@
       default:
     return PS_EXIT_UNKNOWN_ERROR;
-    }    
+    }
     return PS_EXIT_UNKNOWN_ERROR;
 }
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/src/pxtools.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/src/pxtools.h	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/src/pxtools.h	(revision 23594)
@@ -53,5 +53,5 @@
 bool pxSetFaultCode(psDB *dbh, const char *tableName, psMetadata *where, psS16 code);
 
-psExit pxerrorGetExitStatus ();
+psExit pxerrorGetExitStatus(void);
 
 void pxUsage(FILE *stream, int argc, char **argv, const char *modeName, psMetadata *argSet);
@@ -303,5 +303,5 @@
 #define PXOPT_ADD_WHERE_BOOL(name) \
 { \
-    bool value = false;	 \
+    bool value = false;  \
     bool status = false; \
     if ((value = psMetadataLookupBool(&status, config->args, "-" #name))) { \
@@ -370,9 +370,9 @@
 }
 
-#define PXOPT_ADD_WHERE_BOOL_ALIAS(flag,name)	\
+#define PXOPT_ADD_WHERE_BOOL_ALIAS(flag,name)   \
 { \
     bool value = 0; \
     bool status = false; \
-    if ((value = psMetadataLookupBool(&status, config->args, flag))) {	\
+    if ((value = psMetadataLookupBool(&status, config->args, flag))) {  \
         if (!psMetadataAddBOOL(config->where, PS_LIST_TAIL, name, 0, "==", value)) { \
             psError(PS_ERR_UNKNOWN, false, "failed to add item %s", flag); \
@@ -383,9 +383,9 @@
 }
 
-#define PXOPT_ADD_WHERE_S16_ALIAS(flag,name)	\
+#define PXOPT_ADD_WHERE_S16_ALIAS(flag,name)    \
 { \
     psS16 s16 = 0; \
     bool status = false; \
-    if ((s16= psMetadataLookupS16(&status, config->args, flag))) {	\
+    if ((s16= psMetadataLookupS16(&status, config->args, flag))) {      \
         if (!psMetadataAddS16(config->where, PS_LIST_TAIL, name, 0, "==", s16)) { \
             psError(PS_ERR_UNKNOWN, false, "failed to add item %s", flag); \
@@ -396,9 +396,9 @@
 }
 
-#define PXOPT_ADD_WHERE_S32_ALIAS(flag,name)	\
+#define PXOPT_ADD_WHERE_S32_ALIAS(flag,name)    \
 { \
     psS32 s32 = 0; \
     bool status = false; \
-    if ((s32= psMetadataLookupS32(&status, config->args, flag))) {	\
+    if ((s32= psMetadataLookupS32(&status, config->args, flag))) {      \
         if (!psMetadataAddS32(config->where, PS_LIST_TAIL, name, 0, "==", s32)) { \
             psError(PS_ERR_UNKNOWN, false, "failed to add item %s", flag); \
@@ -409,9 +409,9 @@
 }
 
-#define PXOPT_ADD_WHERE_S64_ALIAS(flag,name)	\
+#define PXOPT_ADD_WHERE_S64_ALIAS(flag,name)    \
 { \
     psS64 s64 = 0; \
     bool status = false; \
-    if ((s64= psMetadataLookupS64(&status, config->args, flag))) {	\
+    if ((s64= psMetadataLookupS64(&status, config->args, flag))) {      \
         if (!psMetadataAddS64(config->where, PS_LIST_TAIL, name, 0, "==", s64)) { \
             psError(PS_ERR_UNKNOWN, false, "failed to add item %s", flag); \
@@ -422,9 +422,9 @@
 }
 
-#define PXOPT_ADD_WHERE_F32_ALIAS(flag,name)	\
+#define PXOPT_ADD_WHERE_F32_ALIAS(flag,name)    \
 { \
     psF32 var = 0; \
     bool status = false; \
-    if ((var = psMetadataLookupF32(&status, config->args, flag))) {	\
+    if ((var = psMetadataLookupF32(&status, config->args, flag))) {     \
         if (!isnan(var))  { \
             if (!psMetadataAddF32(config->where, PS_LIST_TAIL, name, 0, "==", var)) { \
@@ -437,9 +437,9 @@
 }
 
-#define PXOPT_ADD_WHERE_F64_ALIAS(flag,name)	\
+#define PXOPT_ADD_WHERE_F64_ALIAS(flag,name)    \
 { \
     psF64 var = 0; \
     bool status = false; \
-    if ((var = psMetadataLookupF64(&status, config->args, flag))) {	\
+    if ((var = psMetadataLookupF64(&status, config->args, flag))) {     \
         if (!isnan(var))  { \
             if (!psMetadataAddF64(config->where, PS_LIST_TAIL, name, 0, "==", var)) { \
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/src/pxwarp.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/src/pxwarp.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/src/pxwarp.c	(revision 23594)
@@ -28,4 +28,92 @@
 #include "pxtools.h"
 #include "pxwarp.h"
+
+bool pxwarpSetSearchArgs (psMetadata *md) {
+    psMetadataAddS64(md, PS_LIST_TAIL, "-fake_id",            0, "search by fake_id", 0);
+    psMetadataAddS64(md, PS_LIST_TAIL, "-cam_id",             0, "search by cam_id", 0);
+    psMetadataAddS64(md, PS_LIST_TAIL, "-chip_id",            0, "search by chip_id", 0);
+    psMetadataAddS64(md, PS_LIST_TAIL, "-exp_id",             0, "search by exp_id", 0);
+    psMetadataAddStr(md, PS_LIST_TAIL, "-exp_name",           0, "search by exp_name", NULL);
+    psMetadataAddStr(md, PS_LIST_TAIL, "-inst",               0, "search for camera", NULL);
+    psMetadataAddStr(md, PS_LIST_TAIL, "-telescope",          0, "search for telescope", NULL);
+    psMetadataAddTime(md, PS_LIST_TAIL, "-dateobs_begin",     0, "search for exposures by time (>=)", NULL);
+    psMetadataAddTime(md, PS_LIST_TAIL, "-dateobs_end",       0, "search for exposures by time (<)", NULL);
+    psMetadataAddStr(md, PS_LIST_TAIL, "-exp_tag",            0, "search by exp_tag", NULL);
+    psMetadataAddStr(md, PS_LIST_TAIL, "-exp_type",           0, "search by exp_type", NULL);
+    psMetadataAddStr(md, PS_LIST_TAIL, "-filelevel",          0, "search by filelevel", NULL);
+    psMetadataAddStr(md, PS_LIST_TAIL, "-filter",             0, "search for filter", NULL);
+    psMetadataAddF64(md, PS_LIST_TAIL, "-airmass_min",        0, "search by min airmass", NAN);
+    psMetadataAddF64(md, PS_LIST_TAIL, "-airmass_max",        0, "search by max airmass", NAN);
+    psMetadataAddF64(md, PS_LIST_TAIL, "-ra_min",             0, "search by min", NAN);
+    psMetadataAddF64(md, PS_LIST_TAIL, "-ra_max",             0, "search by max", NAN);
+    psMetadataAddF64(md, PS_LIST_TAIL, "-decl_min",           0, "search by min", NAN);
+    psMetadataAddF64(md, PS_LIST_TAIL, "-decl_max",           0, "search by max", NAN);
+    psMetadataAddF32(md, PS_LIST_TAIL, "-exp_time_min",       0, "search by min", NAN);
+    psMetadataAddF32(md, PS_LIST_TAIL, "-exp_time_max",       0, "search by max", NAN);
+    psMetadataAddF32(md, PS_LIST_TAIL, "-sat_pixel_frac_min", 0, "search by max fraction of saturated pixels", NAN);
+    psMetadataAddF32(md, PS_LIST_TAIL, "-sat_pixel_frac_max", 0, "search by min fraction of saturated pixels", NAN);
+    psMetadataAddF64(md, PS_LIST_TAIL, "-bg_min",             0, "search by max", NAN);
+    psMetadataAddF64(md, PS_LIST_TAIL, "-bg_max",             0, "search by max", NAN);
+    psMetadataAddF64(md, PS_LIST_TAIL, "-bg_stdev_min",       0, "search by max", NAN);
+    psMetadataAddF64(md, PS_LIST_TAIL, "-bg_stdev_max",       0, "search by max", NAN);
+    psMetadataAddF64(md, PS_LIST_TAIL, "-bg_mean_stdev_min",  0, "search by max", NAN);
+    psMetadataAddF64(md, PS_LIST_TAIL, "-bg_mean_stdev_max",  0, "search by max", NAN);
+    psMetadataAddF64(md, PS_LIST_TAIL, "-alt_min",            0, "search by min", NAN);
+    psMetadataAddF64(md, PS_LIST_TAIL, "-alt_max",            0, "search by max", NAN);
+    psMetadataAddF64(md, PS_LIST_TAIL, "-az_min",             0, "search by min", NAN);
+    psMetadataAddF64(md, PS_LIST_TAIL, "-az_max",             0, "search by max", NAN);
+    psMetadataAddF32(md, PS_LIST_TAIL, "-ccd_temp_min",       0, "search by min ccd tempature", NAN);
+    psMetadataAddF32(md, PS_LIST_TAIL, "-ccd_temp_max",       0, "search by max ccd tempature", NAN);
+    psMetadataAddF64(md, PS_LIST_TAIL, "-posang_min",         0, "search by min rotator position angle", NAN);
+    psMetadataAddF64(md, PS_LIST_TAIL, "-posang_max",         0, "search by max rotator position angle", NAN);
+    psMetadataAddStr(md, PS_LIST_TAIL, "-object",             0, "search by exposure object", NULL);
+    psMetadataAddF32(md, PS_LIST_TAIL, "-solang_min",         0, "search by min solar angle", NAN);
+    psMetadataAddF32(md, PS_LIST_TAIL, "-solang_max",         0, "search by max solar angle", NAN);
+    return true;
+}
+
+bool pxwarpGetSearchArgs (pxConfig *config, psMetadata *where) {
+    PXOPT_COPY_S64(config->args, where, "-fake_id",            "fakeRun.fake_id",       "==");
+    PXOPT_COPY_S64(config->args, where, "-cam_id",             "camRun.cam_id",         "==");
+    PXOPT_COPY_S64(config->args, where, "-chip_id",            "chipRun.chip_id",       "==");
+    PXOPT_COPY_S64(config->args, where, "-exp_id",             "newExp.exp_id",         "==");
+    PXOPT_COPY_STR(config->args, where, "-exp_name",           "rawExp.exp_name",       "==");
+    PXOPT_COPY_STR(config->args, where, "-inst",               "rawExp.camera",         "==");
+    PXOPT_COPY_STR(config->args, where, "-telescope",          "rawExp.telescope",      "==");
+    PXOPT_COPY_TIME(config->args, where, "-dateobs_begin",     "rawExp.dateobs",        ">=");
+    PXOPT_COPY_TIME(config->args, where, "-dateobs_end",       "rawExp.dateobs",        "<=");
+    PXOPT_COPY_STR(config->args, where, "-exp_tag",            "rawExp.exp_tag",        "==");
+    PXOPT_COPY_STR(config->args, where, "-exp_type",           "rawExp.exp_type",       "==");
+    PXOPT_COPY_STR(config->args, where, "-filelevel",          "rawExp.filelevel",      "==");
+    PXOPT_COPY_STR(config->args, where, "-filter",             "rawExp.filter",         "==");
+    PXOPT_COPY_F64(config->args, where, "-airmass_min",        "rawExp.airmass",        ">=");
+    PXOPT_COPY_F64(config->args, where, "-airmass_max",        "rawExp.airmass",        "<");
+    PXOPT_COPY_F64(config->args, where, "-ra_min",             "rawExp.ra",             ">=");
+    PXOPT_COPY_F64(config->args, where, "-ra_max",             "rawExp.ra",             "<");
+    PXOPT_COPY_F64(config->args, where, "-decl_min",           "rawExp.decl",           ">=");
+    PXOPT_COPY_F64(config->args, where, "-decl_max",           "rawExp.decl",           "<");
+    PXOPT_COPY_F32(config->args, where, "-exp_time_min",       "rawExp.exp_time",       ">=");
+    PXOPT_COPY_F32(config->args, where, "-exp_time_max",       "rawExp.exp_time",       "<");
+    PXOPT_COPY_F32(config->args, where, "-sat_pixel_frac_min", "rawExp.sat_pixel_frac", ">=");
+    PXOPT_COPY_F32(config->args, where, "-sat_pixel_frac_max", "rawExp.sat_pixel_frac", "<");
+    PXOPT_COPY_F64(config->args, where, "-bg_min",             "rawExp.bg",             ">=");
+    PXOPT_COPY_F64(config->args, where, "-bg_max",             "rawExp.bg",             "<");
+    PXOPT_COPY_F64(config->args, where, "-bg_stdev_min",       "rawExp.bg_stdev",       ">=");
+    PXOPT_COPY_F64(config->args, where, "-bg_stdev_max",       "rawExp.bg_stdev",       "<");
+    PXOPT_COPY_F64(config->args, where, "-bg_mean_stdev_min",  "rawExp.bg_mean_stdev",  ">=");
+    PXOPT_COPY_F64(config->args, where, "-bg_mean_stdev_max",  "rawExp.bg_mean_stdev",  "<");
+    PXOPT_COPY_F64(config->args, where, "-alt_min",            "rawExp.alt",            ">=");
+    PXOPT_COPY_F64(config->args, where, "-alt_max",            "rawExp.alt",            "<");
+    PXOPT_COPY_F64(config->args, where, "-az_min",             "rawExp.az",             ">=");
+    PXOPT_COPY_F64(config->args, where, "-az_max",             "rawExp.az",             "<");
+    PXOPT_COPY_F32(config->args, where, "-ccd_temp_min",       "rawExp.ccd_temp",       ">=");
+    PXOPT_COPY_F32(config->args, where, "-ccd_temp_max",       "rawExp.ccd_temp",       "<");
+    PXOPT_COPY_F64(config->args, where, "-posang_min",         "rawExp.posang",         ">=");
+    PXOPT_COPY_F64(config->args, where, "-posang_max",         "rawExp.posang",         "<");
+    PXOPT_COPY_STR(config->args, where, "-object",             "rawExp.object",         "==");
+    PXOPT_COPY_F32(config->args, where, "-solang_min",         "rawExp.solang",         ">=");
+    PXOPT_COPY_F32(config->args, where, "-solang_max",         "rawExp.solang",         "<");
+    return true;
+}
 
 bool pxwarpRunSetState(pxConfig *config, psS64 warp_id, const char *state)
@@ -147,5 +235,6 @@
         tess_id,
         end_stage,
-        NULL       // registered
+        NULL,      // registered
+        0          // magicked zero when created will get updated when warpRun goes to 'full'
     )) {
         psError(PS_ERR_UNKNOWN, false, "database error");
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/src/pxwarp.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/src/pxwarp.h	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/src/pxwarp.h	(revision 23594)
@@ -25,4 +25,7 @@
 #include "pxtools.h"
 
+bool pxwarpGetSearchArgs (pxConfig *config, psMetadata *where);
+bool pxwarpSetSearchArgs (psMetadata *md);
+
 bool pxwarpRunSetState(pxConfig *config, psS64 warp_id, const char *state);
 bool pxwarpRunSetStateByQuery(pxConfig *config, psMetadata *where, const char *state);
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/src/pztool.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/src/pztool.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/src/pztool.c	(revision 23594)
@@ -44,7 +44,8 @@
 
 static bool clearcommonfaultsMode(pxConfig *config);
+static bool toadvanceMode(pxConfig *config);
 static bool advanceMode(pxConfig *config);
 
-static bool copydoneCompleteExp(pxConfig *config);
+// static bool copydoneCompleteExp(pxConfig *config);
 static psArray *pzGetPendingCameras(pxConfig *config);
 static psArray *pzArrayZip(psArray *arraySet, psS64 limit);
@@ -80,4 +81,5 @@
         MODECASE(PZTOOL_MODE_REVERTCOPIED, revertcopiedMode);
         MODECASE(PZTOOL_MODE_CLEARCOMMONFAULTS, clearcommonfaultsMode);
+        MODECASE(PZTOOL_MODE_TOADVANCE, toadvanceMode);
         MODECASE(PZTOOL_MODE_ADVANCE, advanceMode);
         default:
@@ -403,6 +405,4 @@
     PXOPT_LOOKUP_S16(code, config->args, "-code", false, false);
 
-    // NOTE : the rest of the command-line args are parsed in copydoneCompleteExp
-
     // start a transaction early so it will contain any row level locks
     if (!psDBTransaction(config->dbh)) {
@@ -463,15 +463,4 @@
     }
 
-#ifdef notdef
-    // we've changed to use -advance instead
-    if (!copydoneCompleteExp(config)) {
-        // rollback
-        if (!psDBRollback(config->dbh)) {
-            psError(PS_ERR_UNKNOWN, false, "database error");
-        }
-        psError(PS_ERR_UNKNOWN, false, "copydoneCompleteExp() failed");
-        return false;
-    }
-#endif
     // point of no return
     if (!psDBCommit(config->dbh)) {
@@ -487,21 +476,10 @@
 }
 
-static bool copydoneCompleteExp(pxConfig *config)
-{
-    // THIS FUNCTION MUST BE INVOKED FROM INSIDE A TRANSACTION!!!
-    
-    PS_ASSERT_PTR_NON_NULL(config, false);
-
-    // XXX this is an ugly hack!
-    // we are passing exp level info to a imfile level mode (-copydone)
-    // these options are thrown away unless we just -copydone'd the last imfile
-    // in an exp.  
- 
-    // optional
-    PXOPT_LOOKUP_STR(workdir, config->args, "-workdir", false, false);
-    PXOPT_LOOKUP_STR(dvodb, config->args, "-dvodb", false, false);
-    PXOPT_LOOKUP_STR(tess_id, config->args, "-tess_id", false, false);
-    PXOPT_LOOKUP_STR(end_stage, config->args, "-end_stage", false, false);
-    PXOPT_LOOKUP_STR(label, config->args, "-label", false, false);
+static bool toadvanceMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    // optional args
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
     PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
 
@@ -517,5 +495,6 @@
     PXOPT_COPY_STR(config->args, where,  "-exp_name", "exp_name", "==");
     PXOPT_COPY_STR(config->args, where,  "-inst", "camera", "==");
-    PXOPT_COPY_STR(config->args, where,  "-telescope", "telescope", "==");
+    PXOPT_COPY_STR(config->args, where,  "-inst", "camera", "==");
+    PXOPT_COPY_STR(config->args, where,  "-label", "label", "==");
 
     if (psListLength(where->list)) {
@@ -525,4 +504,5 @@
     }
     psFree(where);
+
 
     // treat limit == 0 as "no limit"
@@ -552,34 +532,54 @@
     }
 
-   for (long i = 0; i < psArrayLength(output); i++) {
-        psMetadata *row = output->data[i];
-
-        pzDownloadExpRow *doneExp = pzDownloadExpObjectFromMetadata(row);
-        if (!doneExp) {
-            psError(PS_ERR_UNKNOWN, false, "pzDownloadExpObjectFromMetadata() failed");
-            psFree(doneExp);
-            psFree(output);
-            return false;
-        }
-
-        if (!newExpInsert(config->dbh,
-                    0x0,                // exp_id
-                    doneExp->exp_name,  // tmp_exp_name
-                    doneExp->camera,    // tmp_camera
-                    doneExp->telescope, // tmp_telescope
-                    "run",              // state
-                    workdir,            // workdir
-                    "dirty",            // workdir state
-                    NULL,               // reduction class
-                    dvodb,              // dvodb
-                    tess_id,            // tess_id
-                    end_stage,          // end_stage
-                    label,
-                    NULL                // epoch
-                )
+    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 advanceMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    // required
+    PXOPT_LOOKUP_STR(exp_name, config->args, "-exp_name", true, false);
+    PXOPT_LOOKUP_STR(inst, config->args, "-inst", true, false);
+    PXOPT_LOOKUP_STR(telescope, config->args, "-telescope", true, false);
+    PXOPT_LOOKUP_STR(workdir, config->args, "-workdir", true, false);
+
+    // optional
+    PXOPT_LOOKUP_STR(dvodb, config->args, "-dvodb", false, false);
+    PXOPT_LOOKUP_STR(tess_id, config->args, "-tess_id", false, false);
+    PXOPT_LOOKUP_STR(end_stage, config->args, "-end_stage", false, false);
+    PXOPT_LOOKUP_STR(label, config->args, "-label", false, false);
+
+    // start a transaction so it's all rows or nothing
+    if (!psDBTransaction(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    if (!newExpInsert(config->dbh,
+            0x0,        // exp_id
+            exp_name,   // tmp_exp_name
+            inst,       // tmp_camera
+            telescope,  // tmp_telescope
+            "run",      // state
+            workdir,    // workdir
+            "dirty",    // workdir state
+            NULL,       // reduction class
+            dvodb,      // dvodb
+            tess_id,    // tess_id
+            end_stage,  // end_stage
+            label,      // label
+            NULL        // epoch
+            )
         ) {
             psError(PS_ERR_UNKNOWN, false, "database error");
-            psFree(doneExp);
-            psFree(output);
             return false;
         }
@@ -592,8 +592,8 @@
                 "INSERT INTO newImfile"
                 "   SElECT"
-                "       %" PRId64 "," // exp_id
+                "       %" PRId64 ","               // exp_id
                 "       pzDownloadImfile.class_id," // tmp_class_id
-                "       pzDownloadImfile.uri," // uri
-                "       NULL" // epoch
+                "       pzDownloadImfile.uri,"      // uri
+                "       NULL"                       // epoch
                 "   FROM pzDownloadImfile"
                 "   WHERE"
@@ -602,33 +602,33 @@
                 "       AND pzDownloadImfile.telescope = '%s'";
 
-            if (!p_psDBRunQueryF(config->dbh, query, exp_id, doneExp->exp_name, doneExp->camera, doneExp->telescope)) {
-                psError(PS_ERR_UNKNOWN, false, "database error");
-                psFree(doneExp);
-                psFree(output);
-                return false;
-            } 
-
-            // sanity check: we should have inserted at least one row
-            psU64 affected = psDBAffectedRows(config->dbh);
-            if (psDBAffectedRows(config->dbh) < 1) {
-                psError(PS_ERR_UNKNOWN, false, "should have affected at least 1 row but %" PRIu64 " rows were modified", affected);
-                psFree(doneExp);
-                psFree(output);
-                return false;
-            }
-        }
-
-        // set pzDownloadExp.state to 'stop'
-        if (!pzDownloadExpSetState(config, doneExp->exp_name, doneExp->camera, doneExp->telescope, "stop")) {
-            psError(PS_ERR_UNKNOWN, false, "failed to change pzDownloadExp.state for %s:%s:%s", doneExp->exp_name, doneExp->camera, doneExp->telescope);
-            psFree(doneExp);
-            psFree(output);
+        if (!p_psDBRunQueryF(config->dbh, query, exp_id, exp_name, inst, telescope)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
             return false;
-        }
-
-        psFree(doneExp);
-    }
-
-    psFree(output);
+        } 
+
+        // sanity check: we should have inserted at least one row
+        psU64 affected = psDBAffectedRows(config->dbh);
+        if (psDBAffectedRows(config->dbh) < 1) {
+            psError(PS_ERR_UNKNOWN, false, "should have affected at least 1 row but %" PRIu64 " rows were modified", affected);
+            return false;
+        }
+    }
+
+    // set pzDownloadExp.state to 'stop'
+    if (!pzDownloadExpSetState(config, exp_name, inst, telescope, "stop")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to change pzDownloadExp.state for %s:%s:%s", exp_name, inst, telescope);
+        return false;
+    }
+
+    // point of no return
+    if (!psDBCommit(config->dbh)) {
+        // rollback
+        if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+        }
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
 
     return true;
@@ -872,39 +872,4 @@
 }
 
-static bool advanceMode(pxConfig *config)
-{
-    PS_ASSERT_PTR_NON_NULL(config, false);
-
-    // NOTE : the command-line args are parsed in copydoneCompleteExp
-
-    // start a transaction so it's all rows or nothing
-    if (!psDBTransaction(config->dbh)) {
-        psError(PS_ERR_UNKNOWN, false, "database error");
-        return false;
-    }
-
-    if (!copydoneCompleteExp(config)) {
-        // rollback
-        if (!psDBRollback(config->dbh)) {
-            psError(PS_ERR_UNKNOWN, false, "database error");
-        }
-        psError(PS_ERR_UNKNOWN, false, "copydoneCompleteExp() failed");
-        return false;
-    }
-
-    // point of no return
-    if (!psDBCommit(config->dbh)) {
-        // rollback
-        if (!psDBRollback(config->dbh)) {
-            psError(PS_ERR_UNKNOWN, false, "database error");
-        }
-        psError(PS_ERR_UNKNOWN, false, "database error");
-        return false;
-    }
-
-
-    return true;
-}
-
 
 static bool pzDownloadExpSetState(pxConfig *config, const char *exp_name, const char *camera, const char *telescope, const char *state)
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/src/pztool.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/src/pztool.h	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/src/pztool.h	(revision 23594)
@@ -35,4 +35,5 @@
     PZTOOL_MODE_REVERTCOPIED,
     PZTOOL_MODE_CLEARCOMMONFAULTS,
+    PZTOOL_MODE_TOADVANCE,
     PZTOOL_MODE_ADVANCE
 } pztoolMode;
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/src/pztoolConfig.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/src/pztoolConfig.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/src/pztoolConfig.c	(revision 23594)
@@ -97,4 +97,6 @@
     psMetadataAddS16(copydoneArgs, PS_LIST_TAIL, "-code",  0,            "set fault code", 0);
     psMetadataAddBool(copydoneArgs, PS_LIST_TAIL, "-row_lock", 0,     "lock pzDownImfile rows while advancing an exposure", false);
+    // XXX: remove this once advance is fixed
+    psMetadataAddU64(copydoneArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
 
     // -copied
@@ -129,16 +131,24 @@
     // -clearcommonfaults
     psMetadata *clearcommonfaultsArgs = psMetadataAlloc();
+    //
+    // -toadvance
+    psMetadata *toadvanceArgs = psMetadataAlloc();
+    psMetadataAddStr(toadvanceArgs, PS_LIST_TAIL, "-exp_name", 0,      "define exposure ID", NULL); 
+    psMetadataAddStr(toadvanceArgs, PS_LIST_TAIL, "-inst", 0,          "define camera ID", NULL); 
+    psMetadataAddStr(toadvanceArgs, PS_LIST_TAIL, "-telescope", 0,     "define telescope ID", NULL); 
+    psMetadataAddStr(toadvanceArgs, PS_LIST_TAIL, "-label",  0,        "define the label for the chip stage", NULL);
+    psMetadataAddBool(toadvanceArgs, PS_LIST_TAIL, "-simple",  0,      "use the simple output format", false);
+    psMetadataAddU64(toadvanceArgs, PS_LIST_TAIL, "-limit",  0,        "limit result set to N items", 0);
 
     // -advance
     psMetadata *advanceArgs = psMetadataAlloc();
-    psMetadataAddStr(advanceArgs, PS_LIST_TAIL, "-exp_name", 0,            "define exposure ID", NULL); 
-    psMetadataAddStr(advanceArgs, PS_LIST_TAIL, "-inst", 0,            "define camera ID", NULL); 
-    psMetadataAddStr(advanceArgs, PS_LIST_TAIL, "-telescope", 0,            "define telescope ID", NULL); 
-    psMetadataAddStr(advanceArgs, PS_LIST_TAIL, "-workdir",  0,        "define the \"default\" workdir for this exposure", NULL);
-    psMetadataAddStr(advanceArgs, PS_LIST_TAIL, "-dvodb",  0,        "define the dvodb for the next processing step", NULL);
-    psMetadataAddStr(advanceArgs, PS_LIST_TAIL, "-tess_id",  0,        "define the tess_id for the next processing step", NULL);
-    psMetadataAddStr(advanceArgs, PS_LIST_TAIL, "-end_stage",  0,        "define the end goal processing step", NULL);
-    psMetadataAddStr(advanceArgs, PS_LIST_TAIL, "-label",  0,        "define the label for the chip stage", NULL);
-    psMetadataAddU64(advanceArgs, PS_LIST_TAIL, "-limit",  0,         "limit result set to N items", 0);
+    psMetadataAddStr(advanceArgs, PS_LIST_TAIL, "-exp_name", 0,   "define exposure ID (required)", NULL); 
+    psMetadataAddStr(advanceArgs, PS_LIST_TAIL, "-inst", 0,       "define camera ID (required)", NULL); 
+    psMetadataAddStr(advanceArgs, PS_LIST_TAIL, "-telescope", 0,  "define telescope ID (required)", NULL); 
+    psMetadataAddStr(advanceArgs, PS_LIST_TAIL, "-workdir",  0,   "define the \"default\" workdir for this exposure (required)", NULL);
+    psMetadataAddStr(advanceArgs, PS_LIST_TAIL, "-dvodb",  0,     "define the dvodb for the next processing step", NULL);
+    psMetadataAddStr(advanceArgs, PS_LIST_TAIL, "-tess_id",  0,   "define the tess_id for the next processing step", NULL);
+    psMetadataAddStr(advanceArgs, PS_LIST_TAIL, "-end_stage",  0, "define the end goal processing step", NULL);
+    psMetadataAddStr(advanceArgs, PS_LIST_TAIL, "-label",  0,     "define the label for the chip stage", NULL);
 
     psMetadata *argSets = psMetadataAlloc();
@@ -155,4 +165,5 @@
     PXOPT_ADD_MODE("-revertcopied",    "", PZTOOL_MODE_REVERTCOPIED,revertcopiedArgs);
     PXOPT_ADD_MODE("-clearcommonfaults","", PZTOOL_MODE_CLEARCOMMONFAULTS,clearcommonfaultsArgs);
+    PXOPT_ADD_MODE("-toadvance",          "", PZTOOL_MODE_TOADVANCE,    toadvanceArgs);
     PXOPT_ADD_MODE("-advance",          "", PZTOOL_MODE_ADVANCE,    advanceArgs);
 
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/src/regtool.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/src/regtool.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/src/regtool.c	(revision 23594)
@@ -319,7 +319,9 @@
 
     psMetadata *where = psMetadataAlloc();
-    PXOPT_COPY_S64(config->args, where,  "-exp_id", "exp_id", "==");
-    PXOPT_COPY_STR(config->args, where,  "-exp_name", "exp_name", "==");
-    PXOPT_COPY_STR(config->args, where,  "-class_id", "class_id", "==");
+    PXOPT_COPY_S64(config->args, where,  "-exp_id",        "exp_id",   "==");
+    PXOPT_COPY_STR(config->args, where,  "-exp_name",      "exp_name", "==");
+    PXOPT_COPY_STR(config->args, where,  "-class_id",      "class_id", "==");
+    PXOPT_COPY_TIME(config->args, where, "-dateobs_begin", "dateobs",  ">=");
+    PXOPT_COPY_TIME(config->args, where, "-dateobs_end",   "dateobs",  "<=");
 
     PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
@@ -407,4 +409,6 @@
     PXOPT_COPY_STR(config->args, where,  "-class_id",     "class_id", "==");
     PXOPT_COPY_S16(config->args, where,  "-code",         "fault", "==");
+    PXOPT_COPY_S64(config->args, where,  "-exp_id_begin", "exp_id", ">=");
+    PXOPT_COPY_S64(config->args, where,  "-exp_id_end", "exp_id", "<=");
 
     psString query = pxDataGet("regtool_revertprocessedimfile.sql");
@@ -419,4 +423,8 @@
         psStringAppend(&query, " AND %s", whereClause);
         psFree(whereClause);
+    } else {
+        psFree(where);
+        psError(PXTOOLS_ERR_DATA, false, "search parameters are required");
+        return false;
     }
     psFree(where);
@@ -429,8 +437,7 @@
     psFree(query);
 
-    if (psDBAffectedRows(config->dbh) < 1) {
-        psError(PS_ERR_UNKNOWN, false, "should have affected atleast 1 row");
-        return false;
-    }
+    int numUpdated = psDBAffectedRows(config->dbh);
+
+    psLogMsg("regtool", PS_LOG_INFO, "Updated %d rawImfile", numUpdated);
 
     return true;
@@ -441,4 +448,19 @@
 {
     PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_S64(exp_id, config->args, "-exp_id", true, false);
+    PXOPT_LOOKUP_STR(class_id, config->args, "-class_id", true, false);
+
+    PXOPT_LOOKUP_S16(code, config->args, "-code", false, false);
+    PXOPT_LOOKUP_F64(user_1, config->args, "-user_1", false, false);
+
+    if ((code == INT16_MAX) && !isfinite(user_1)) { 
+        psError(PS_ERR_UNKNOWN, false, "one of -code or -user_1 must be selected");
+        return false;
+    }
+    if ((code != INT16_MAX) && isfinite(user_1)) { 
+        psError(PS_ERR_UNKNOWN, false, "only one of -code or -user_1 must be selected");
+        return false;
+    }
 
     psMetadata *where = psMetadataAlloc();
@@ -446,10 +468,26 @@
     PXOPT_COPY_STR(config->args, where,  "-class_id",     "class_id", "==");
 
-    PXOPT_LOOKUP_S16(code, config->args, "-code", true, false);
-
-    if (!pxSetFaultCode(config->dbh, "rawImfile", where, code)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to set set fault flag");
-        psFree (where);
-        return false;
+    if (code != INT16_MAX) {
+	// this is fairly dangerous : can set all if the where is not set...
+	if (!pxSetFaultCode(config->dbh, "rawImfile", where, code)) {
+	    psError(PS_ERR_UNKNOWN, false, "failed to set set fault flag");
+	    psFree (where);
+	    return false;
+	}
+    }
+
+    if (isfinite(user_1)) {
+	psString query = pxDataGet("regtool_updateprocessedimfile.sql");
+	if (!query) {
+	    psError(PXTOOLS_ERR_DATA, false, "failed to retreive SQL statement");
+	    return false;
+	}
+
+	if (!p_psDBRunQueryF(config->dbh, query, user_1, exp_id, class_id)) {
+	    psError(PS_ERR_UNKNOWN, false, "database error");
+	    psFree(query);
+	    return false;
+	}
+	psFree(query);
     }
     psFree (where);
@@ -764,5 +802,6 @@
         hostname,
         code,
-        NULL
+        NULL,
+        0
     )) {
         // rollback
@@ -806,5 +845,5 @@
                 exp_id,
                 workdir,
-            label,
+                label,
                 reduction,
                 NULL, // expgroup
@@ -957,4 +996,6 @@
     PXOPT_COPY_S64(config->args, where,  "-exp_id",       "exp_id", "==");
     PXOPT_COPY_S16(config->args, where,  "-code",         "fault", "==");
+    PXOPT_COPY_S64(config->args, where,  "-exp_id_begin", "exp_id", ">=");
+    PXOPT_COPY_S64(config->args, where,  "-exp_id_end", "exp_id", "<=");
 
     psString query = pxDataGet("regtool_revertprocessedexp.sql");
@@ -968,6 +1009,9 @@
         psString whereClause = psDBGenerateWhereConditionSQL(where, "rawExp");
         psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    } else {
         psFree(where);
-        psFree(whereClause);
+        psError(PXTOOLS_ERR_DATA, false, "search parameters are required");
+        return false;
     }
     psFree(where);
@@ -980,8 +1024,7 @@
     psFree(query);
 
-    if (psDBAffectedRows(config->dbh) < 1) {
-        psError(PS_ERR_UNKNOWN, false, "should have affected atleast 1 row");
-        return false;
-    }
+    int numUpdated = psDBAffectedRows(config->dbh);
+
+    psLogMsg("regtool", PS_LOG_INFO, "Updated %d rawExp", numUpdated);
 
     return true;
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/src/regtoolConfig.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/src/regtoolConfig.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/src/regtoolConfig.c	(revision 23594)
@@ -129,4 +129,6 @@
     ADD_OPT(Str,  processedimfileArgs, "-exp_name",  "search by exposure name",               NULL);
     ADD_OPT(Str,  processedimfileArgs, "-class_id",  "search by class ID",                    NULL);
+    ADD_OPT(Time, processedimfileArgs, "-dateobs_begin", "search for exposures by time (>=)", NULL);
+    ADD_OPT(Time, processedimfileArgs, "-dateobs_end", "search for exposures by time (<)", NULL);
     ADD_OPT(U64,  processedimfileArgs, "-limit",     "limit result set to N items",           0);
     ADD_OPT(Bool, processedimfileArgs, "-faulted",   "only return imfiles with a fault status set", false);
@@ -135,8 +137,10 @@
     // -revertprocessedimfile
     psMetadata *revertprocessedimfileArgs = psMetadataAlloc();
-    ADD_OPT(S64, revertprocessedimfileArgs, "-exp_id",        "search by exposure ID (required)", 0);
+    ADD_OPT(S64, revertprocessedimfileArgs, "-exp_id",        "search by exposure ID", 0);
     ADD_OPT(Str, revertprocessedimfileArgs, "-tmp_class_id",  "searcy by temp. class ID", NULL);
     ADD_OPT(Str, revertprocessedimfileArgs, "-class_id",      "search by class ID", NULL);
     ADD_OPT(S16, revertprocessedimfileArgs, "-code",          "search by fault code", 0);
+    ADD_OPT(S64, revertprocessedimfileArgs, "-exp_id_begin",  "search by exposure ID", 0);
+    ADD_OPT(S64, revertprocessedimfileArgs, "-exp_id_end",    "search by exposure ID", 0);
 
     // -updateprocessedimfile
@@ -144,5 +148,6 @@
     ADD_OPT(S64, updateprocessedimfileArgs, "-exp_id",        "search by exposure ID", 0);
     ADD_OPT(Str, updateprocessedimfileArgs, "-class_id",      "search by class ID", NULL);
-    ADD_OPT(S16, updateprocessedimfileArgs, "-code",          "set fault code (required)", INT16_MAX);
+    ADD_OPT(F64, updateprocessedimfileArgs, "-user_1",        "set user stat (1)", NAN);
+    ADD_OPT(S16, updateprocessedimfileArgs, "-code",          "set fault code", INT16_MAX);
 
     // -pendingexp
@@ -270,6 +275,8 @@
     // -revertprocessedexp
     psMetadata *revertprocessedexpArgs = psMetadataAlloc();
-    psMetadataAddS64(revertprocessedexpArgs, PS_LIST_TAIL, "-exp_id",   0,            "search by exposure ID (required)", 0);
+    psMetadataAddS64(revertprocessedexpArgs, PS_LIST_TAIL, "-exp_id",   0,            "search by exposure ID", 0);
     psMetadataAddS16(revertprocessedexpArgs, PS_LIST_TAIL, "-code",     0,            "search by fault code", 0);
+    psMetadataAddS64(revertprocessedexpArgs, PS_LIST_TAIL, "-exp_id_begin",   0,      "search by exposure ID", 0);
+    psMetadataAddS64(revertprocessedexpArgs, PS_LIST_TAIL, "-exp_id_end",   0,      "search by exposure ID", 0);
 
     // -updatedprocessedexp
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/src/warptool.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/src/warptool.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/src/warptool.c	(revision 23594)
@@ -157,5 +157,6 @@
             tess_id,
             end_stage,
-            registered
+            registered,
+            false       // magicked
     );
     if (!warpRun) {
@@ -190,46 +191,7 @@
 
     psMetadata *where = psMetadataAlloc();
-    PXOPT_COPY_S64(config->args, where, "-fake_id", "fakeRun.fake_id", "==");
-    PXOPT_COPY_S64(config->args, where, "-cam_id", "camRun.cam_id", "==");
-    PXOPT_COPY_S64(config->args, where, "-chip_id", "chipRun.chip_id", "==");
-    PXOPT_COPY_S64(config->args, where, "-exp_id", "newExp.exp_id", "==");
-    PXOPT_COPY_STR(config->args, where, "-exp_name", "exp_name", "==");
-    PXOPT_COPY_STR(config->args, where, "-inst", "camera", "==");
-    PXOPT_COPY_STR(config->args, where, "-telescope", "telescope", "==");
-    PXOPT_COPY_TIME(config->args, where, "-dateobs_begin", "dateobs", ">=");
-    PXOPT_COPY_TIME(config->args, where, "-dateobs_end", "dateobs", "<=");
-    PXOPT_COPY_STR(config->args, where, "-exp_tag", "exp_tag", "==");
-    PXOPT_COPY_STR(config->args, where, "-exp_type", "exp_type", "==");
-    PXOPT_COPY_STR(config->args, where, "-filelevel", "filelevel", "==");
-    PXOPT_COPY_STR(config->args, where, "-reduction", "reduction", "==");
-    PXOPT_COPY_STR(config->args, where, "-filter", "filter", "==");
-    PXOPT_COPY_F64(config->args, where, "-airmass_min", "airmass", ">=");
-    PXOPT_COPY_F64(config->args, where, "-airmass_max", "airmass", "<");
-    PXOPT_COPY_F64(config->args, where, "-ra_min", "ra", ">=");
-    PXOPT_COPY_F64(config->args, where, "-ra_max", "ra", "<");
-    PXOPT_COPY_F64(config->args, where, "-decl_min", "decl", ">=");
-    PXOPT_COPY_F64(config->args, where, "-decl_max", "decl", "<");
-    PXOPT_COPY_F32(config->args, where, "-exp_time_min", "exp_time", ">=");
-    PXOPT_COPY_F32(config->args, where, "-exp_time_max", "exp_time", "<");
-    PXOPT_COPY_F32(config->args, where, "-sat_pixel_frac_min", "sat_pixel_frac", ">=");
-    PXOPT_COPY_F32(config->args, where, "-sat_pixel_frac_max", "sat_pixel_frac", "<");
-    PXOPT_COPY_F64(config->args, where, "-bg_min", "bt", ">=");
-    PXOPT_COPY_F64(config->args, where, "-bg_max", "bt", "<");
-    PXOPT_COPY_F64(config->args, where, "-bg_stdev_min", "bg_stdev", ">=");
-    PXOPT_COPY_F64(config->args, where, "-bg_stdev_max", "bg_stdev", "<");
-    PXOPT_COPY_F64(config->args, where, "-bg_mean_stdev_min", "bg_mean_stdev", ">=");
-    PXOPT_COPY_F64(config->args, where, "-bg_mean_stdev_max", "bg_mean_stdev", "<");
-    PXOPT_COPY_F64(config->args, where, "-alt_min", "alt", ">=");
-    PXOPT_COPY_F64(config->args, where, "-alt_max", "alt", "<");
-    PXOPT_COPY_F64(config->args, where, "-az_min", "az", ">=");
-    PXOPT_COPY_F64(config->args, where, "-az_max", "az", "<");
-    PXOPT_COPY_F32(config->args, where, "-ccd_temp_min", "ccd_temp", ">=");
-    PXOPT_COPY_F32(config->args, where, "-ccd_temp_max", "ccd_temp", "<");
-    PXOPT_COPY_F64(config->args, where, "-posang_min", "posang", ">=");
-    PXOPT_COPY_F64(config->args, where, "-posang_max", "posang", "<");
-    PXOPT_COPY_STR(config->args, where, "-object", "object", "==");
-    PXOPT_COPY_F32(config->args, where, "-solang_min", "solang", ">=");
-    PXOPT_COPY_F32(config->args, where, "-solang_max", "solang", "<");
-    PXOPT_COPY_STR(config->args, where, "-label", "label", "==");
+    pxwarpGetSearchArgs (config, where);
+    PXOPT_COPY_STR(config->args, where, "-reduction", "fakeRun.reduction", "==");
+    PXOPT_COPY_STR(config->args, where, "-label",     "fakeRun.label",     "==");
 
     if (!psListLength(where->list) &&
@@ -337,45 +299,9 @@
 
     psMetadata *where = psMetadataAlloc();
-    PXOPT_COPY_S64(config->args, where, "-warp_id", "warp_id", "==");
-    PXOPT_COPY_S64(config->args, where, "-fake_id", "fake_id", "==");
-    PXOPT_COPY_S64(config->args, where, "-chip_id", "chip_id", "==");
-    PXOPT_COPY_S64(config->args, where, "-exp_id", "exp_id", "==");
-    PXOPT_COPY_STR(config->args, where, "-exp_name", "exp_name", "==");
-    PXOPT_COPY_STR(config->args, where, "-inst", "camera", "==");
-    PXOPT_COPY_STR(config->args, where, "-telescope", "telescope", "==");
-    PXOPT_COPY_TIME(config->args, where, "-dateobs_begin", "dateobs", ">=");
-    PXOPT_COPY_TIME(config->args, where, "-dateobs_end", "dateobs", "<=");
-    PXOPT_COPY_STR(config->args, where, "-exp_tag", "exp_tag", "==");
-    PXOPT_COPY_STR(config->args, where, "-exp_type", "exp_type", "==");
-    PXOPT_COPY_STR(config->args, where, "-filelevel", "filelevel", "==");
-    PXOPT_COPY_STR(config->args, where, "-reduction", "reduction", "==");
-    PXOPT_COPY_STR(config->args, where, "-filter", "filter", "==");
-    PXOPT_COPY_F32(config->args, where, "-airmass_min", "airmass", ">=");
-    PXOPT_COPY_F32(config->args, where, "-airmass_max", "airmass", "<");
-    PXOPT_COPY_F64(config->args, where, "-ra_min", "ra", ">=");
-    PXOPT_COPY_F64(config->args, where, "-ra_max", "ra", "<");
-    PXOPT_COPY_F64(config->args, where, "-decl_min", "decl", ">=");
-    PXOPT_COPY_F64(config->args, where, "-decl_max", "decl", "<");
-    PXOPT_COPY_F32(config->args, where, "-exp_time_min", "exp_time", ">=");
-    PXOPT_COPY_F32(config->args, where, "-exp_time_max", "exp_time", "<");
-    PXOPT_COPY_F32(config->args, where, "-sat_pixel_frac_min", "sat_pixel_frac", ">=");
-    PXOPT_COPY_F32(config->args, where, "-sat_pixel_frac_max", "sat_pixel_frac", "<");
-    PXOPT_COPY_F64(config->args, where, "-bg_min", "bt", ">=");
-    PXOPT_COPY_F64(config->args, where, "-bg_max", "bt", "<");
-    PXOPT_COPY_F64(config->args, where, "-bg_stdev_min", "bg_stdev", ">=");
-    PXOPT_COPY_F64(config->args, where, "-bg_stdev_max", "bg_stdev", "<");
-    PXOPT_COPY_F64(config->args, where, "-bg_mean_stdev_min", "bg_mean_stdev", ">=");
-    PXOPT_COPY_F64(config->args, where, "-bg_mean_stdev_max", "bg_mean_stdev", "<");
-    PXOPT_COPY_F64(config->args, where, "-alt_min", "alt", ">=");
-    PXOPT_COPY_F64(config->args, where, "-alt_max", "alt", "<");
-    PXOPT_COPY_F64(config->args, where, "-az_min", "az", ">=");
-    PXOPT_COPY_F64(config->args, where, "-az_max", "az", "<");
-    PXOPT_COPY_F64(config->args, where, "-ccd_temp_min", "ccd_temp", ">=");
-    PXOPT_COPY_F64(config->args, where, "-ccd_temp_max", "ccd_temp", "<");
-    PXOPT_COPY_F64(config->args, where, "-posang_min", "posang", ">=");
-    PXOPT_COPY_F64(config->args, where, "-posang_max", "posang", "<");
-    PXOPT_COPY_STR(config->args, where, "-object", "object", "==");
-    PXOPT_COPY_F32(config->args, where, "-solang_min", "solang", ">=");
-    PXOPT_COPY_F32(config->args, where, "-solang_max", "solang", "<");
+    pxwarpGetSearchArgs (config, where);
+    PXOPT_COPY_S64(config->args, where, "-warp_id",   "warpRun.warp_id",   "==");
+    PXOPT_COPY_STR(config->args, where, "-reduction", "warpRun.reduction", "==");
+    PXOPT_COPY_STR(config->args, where, "-label",     "warpRun.label", 	   "==");
+    PXOPT_COPY_STR(config->args, where, "-state",     "warpRun.state", 	   "==");
 
     if (!psListLength(where->list)
@@ -387,6 +313,6 @@
     }
 
-    PXOPT_LOOKUP_STR(state, config->args, "-state", false, false);
-    PXOPT_LOOKUP_STR(label, config->args, "-label", false, false);
+    PXOPT_LOOKUP_STR(state, config->args, "-set_state", false, false);
+    PXOPT_LOOKUP_STR(label, config->args, "-set_label", false, false);
 
     if ((!state) && (!label)) {
@@ -934,4 +860,5 @@
     PXOPT_LOOKUP_F32(good_frac, config->args, "-good_frac", false, false);
     PXOPT_LOOKUP_BOOL(accept, config->args, "-accept", false);
+    PXOPT_LOOKUP_BOOL(magicked, config->args, "-magicked", false);
 
     // default values
@@ -965,5 +892,5 @@
                            !accept,
                            code,
-                           0            // magic_ds_id
+                           magicked
         )) {
         if (!psDBRollback(config->dbh)) {
@@ -995,103 +922,65 @@
     PS_ASSERT_PTR_NON_NULL(config, false);
 
-    // XXX this SQL has not been broken out to into seperate files as the MYSQL
-    // < 5 & MYSQL 5 versions need to be kept in sync
-
-#undef MYSQL5
-#if MYSQL5
-    // XXX at MySQL 4.1.21 (probably all of 4.1.x) chokes and dies on this
-    // statement as it thinks it is trying to select from the table being
-    // updated. The 4.1 manual says that nested sub-queries are explicited
-    // allowed to do this with update statements as a temporary table is
-    // created so that you are not actually selecting from the table you are
-    // modifying.
-    char *query =
-        "UPDATE warpRun\n"
-        "   SET warpRun.state = 'stop'\n"
-        " WHERE\n"
-        "   warpRun.warp_id =\n"
-        "   (SELECT DISTINCT\n"
-        "       warp_id\n"
-        "   FROM\n"
-        "       (SELECT DISTINCT\n"
-        "           warpRun.warp_id,\n"
-        "           warpSkyCellMap.warp_id as foo,\n"
-        "           warpSkyfile.warp_id as bar\n"
-        "       FROM warpRun\n"
-        "       JOIN warpSkyCellMap\n"
-        "           USING(warp_id)\n"
-        "       LEFT JOIN warpSkyfile\n"
-        "           USING(warp_id, skycell_id, tess_id)\n"
-        "       WHERE\n"
-        "           warpRun.state = 'new'\n"
-        "       GROUP BY\n"
-        "           warpRun.warp_id\n"
-        "       HAVING\n"
-        "       COUNT(warpSkyCellMap.warp_id) = COUNT(warpSkyfile.warp_id)\n"
-        "       ) as Foo\n"
-        "   )\n";
+    psString query = pxDataGet("warptool_finished_run_select.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_DATA, false, "failed to retrieve SQL statement");
+        return false;
+    }
 
     if (!p_psDBRunQuery(config->dbh, query)) {
         psError(PS_ERR_UNKNOWN, false, "database error");
-        return false;
-    }
-#else // if MYSQL5
-{
-    char *query =
-        "CREATE TEMPORARY TABLE finished\n"
-        " (warp_id INT, PRIMARY KEY(warp_id)) ENGINE=MEMORY\n";
-
-    if (!p_psDBRunQuery(config->dbh, query)) {
-        psError(PS_ERR_UNKNOWN, false, "database error");
-        return false;
-    }
-}
-
-{
-    char *query =
-        "INSERT INTO finished\n"
-        " SELECT\n"
-        "   warp_id\n"
-        " FROM\n"
-        "   (SELECT DISTINCT\n"
-        "       warpRun.warp_id,\n"
-        "       warpSkyCellMap.warp_id as foo,\n"
-        "       warpSkyfile.warp_id as bar\n"
-        "   FROM warpRun\n"
-        "   JOIN warpSkyCellMap\n"
-        "       USING(warp_id)\n"
-        "   LEFT JOIN warpSkyfile\n"
-        "       USING(warp_id, skycell_id)\n"
-        "   WHERE\n"
-        "       warpRun.state = 'new'\n"
-        "   GROUP BY\n"
-        "       warpRun.warp_id\n"
-        "   HAVING\n"
-        "       COUNT(warpSkyCellMap.warp_id) = COUNT(warpSkyfile.warp_id)\n"
-        " ) as Foo \n";
-
-    if (!p_psDBRunQuery(config->dbh, query)) {
-        psError(PS_ERR_UNKNOWN, false, "database error");
-        return false;
-    }
-}
-
-{
-    char *query =
-        "UPDATE warpRun\n"
-        "   SET warpRun.state = 'full'\n"
-        " WHERE\n"
-        "   warpRun.warp_id =\n"
-        "   (SELECT DISTINCT\n"
-        "       warp_id\n"
-        "   FROM finished\n"
-        "   )\n";
-
-    if (!p_psDBRunQuery(config->dbh, query)) {
-        psError(PS_ERR_UNKNOWN, false, "database error");
-        return false;
-    }
-}
-#endif // if MYSQL5
+        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("warptool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    query = pxDataGet("warptool_finish_run.sql");
+    for (long i = 0; i < psArrayLength(output); i++) {
+        psMetadata *row = output->data[i];
+
+        bool status;
+        psS64 warp_id = psMetadataLookupS64(&status, row, "warp_id");
+        if (!status) {
+            psError(PS_ERR_UNKNOWN, false, "failed to look up value for warp_id");
+            psFree(output);
+            psFree(query);
+            return false;
+        }
+        psS32 magicked = psMetadataLookupS64(&status, row, "magicked");
+        if (!status) {
+            psError(PS_ERR_UNKNOWN, false, "failed to look up value for magicked");
+            psFree(output);
+            psFree(query);
+            return false;
+        }
+        if (!p_psDBRunQueryF(config->dbh, query, magicked, warp_id)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            psFree(output);
+            psFree(query);
+            return false;
+        }
+
+        psS64 numUpdated = psDBAffectedRows(config->dbh);
+
+        if (numUpdated != 1) {
+            psError(PS_ERR_UNKNOWN, false, "should have affected 1 row");
+            psFree(query);
+            psFree(output);
+            return false;
+        }
+    }
+    psFree(output);
+    psFree(query);
 
     return true;
@@ -1181,51 +1070,11 @@
 
     psMetadata *where = psMetadataAlloc();
-    PXOPT_COPY_S64(config->args, where, "-warp_id", "warpSkyfile.warp_id", "==");
+    pxwarpGetSearchArgs (config, where);
+    PXOPT_COPY_S64(config->args, where, "-warp_id",    "warpSkyfile.warp_id", "==");
     PXOPT_COPY_STR(config->args, where, "-skycell_id", "warpSkyfile.skycell_id", "==");
-    PXOPT_COPY_STR(config->args, where, "-tess_id", "warpSkyfile.tess_id", "==");
-    PXOPT_COPY_S64(config->args, where, "-fake_id", "fakeRun.fake_id", "==");
-    PXOPT_COPY_S64(config->args, where, "-chip_id", "chipRun.chip_id", "==");
-    PXOPT_COPY_S64(config->args, where, "-exp_id", "rawExp.exp_id", "==");
-
-    PXOPT_COPY_STR(config->args, where, "-exp_name", "rawExp.exp_name", "==");
-    PXOPT_COPY_STR(config->args, where, "-label", "warpRun.label", "==");
-    PXOPT_COPY_STR(config->args, where, "-inst", "rawExp.camera", "==");
-    PXOPT_COPY_STR(config->args, where, "-telescope", "rawExp.telescope", "==");
-    PXOPT_COPY_TIME(config->args, where, "-dateobs_begin", "rawExp.dateobs", ">=");
-    PXOPT_COPY_TIME(config->args, where, "-dateobs_end", "rawExp.dateobs", "<=");
-    PXOPT_COPY_STR(config->args, where, "-exp_tag", "rawExp.exp_tag", "==");
-    PXOPT_COPY_STR(config->args, where, "-exp_type", "rawExp.exp_type", "==");
-    PXOPT_COPY_STR(config->args, where, "-filelevel", "rawExp.filelevel", "==");
-    PXOPT_COPY_STR(config->args, where, "-reduction", "rawExp.reduction", "==");
-    PXOPT_COPY_STR(config->args, where, "-filter", "rawExp.filter", "==");
-
-    PXOPT_COPY_F32(config->args, where, "-airmass_min", "rawExp.airmass", ">=");
-    PXOPT_COPY_F32(config->args, where, "-airmass_max", "rawExp.airmass", "<");
-    PXOPT_COPY_F64(config->args, where, "-ra_min", "rawExp.ra", ">=");
-    PXOPT_COPY_F64(config->args, where, "-ra_max", "rawExp.ra", "<");
-    PXOPT_COPY_F64(config->args, where, "-decl_min", "rawExp.decl", ">=");
-    PXOPT_COPY_F64(config->args, where, "-decl_max", "rawExp.decl", "<");
-    PXOPT_COPY_F32(config->args, where, "-exp_time_min", "rawExp.exp_time", ">=");
-    PXOPT_COPY_F32(config->args, where, "-exp_time_max", "rawExp.exp_time", "<");
-    PXOPT_COPY_F32(config->args, where, "-sat_pixel_frac_min", "rawExp.sat_pixel_frac", ">=");
-    PXOPT_COPY_F32(config->args, where, "-sat_pixel_frac_max", "rawExp.sat_pixel_frac", "<");
-    PXOPT_COPY_F64(config->args, where, "-bg_min", "rawExp.bg", ">=");
-    PXOPT_COPY_F64(config->args, where, "-bg_max", "rawExp.bg", "<");
-    PXOPT_COPY_F64(config->args, where, "-bg_stdev_min", "rawExp.bg_stdev", ">=");
-    PXOPT_COPY_F64(config->args, where, "-bg_stdev_max", "rawExp.bg_stdev", "<");
-    PXOPT_COPY_F64(config->args, where, "-bg_mean_stdev_min", "rawExp.bg_mean_stdev", ">=");
-    PXOPT_COPY_F64(config->args, where, "-bg_mean_stdev_max", "rawExp.bg_mean_stdev", "<");
-    PXOPT_COPY_F64(config->args, where, "-alt_min", "rawExp.alt", ">=");
-    PXOPT_COPY_F64(config->args, where, "-alt_max", "rawExp.alt", "<");
-    PXOPT_COPY_F64(config->args, where, "-az_min", "rawExp.az", ">=");
-    PXOPT_COPY_F64(config->args, where, "-az_max", "rawExp.az", "<");
-    PXOPT_COPY_F64(config->args, where, "-ccd_temp_min", "rawExp.ccd_temp", ">=");
-    PXOPT_COPY_F64(config->args, where, "-ccd_temp_max", "rawExp.ccd_temp", "<");
-    PXOPT_COPY_F64(config->args, where, "-posang_min", "rawExp.posang", ">=");
-    PXOPT_COPY_F64(config->args, where, "-posang_max", "rawExp.posang", "<");
-    PXOPT_COPY_STR(config->args, where, "-object", "rawExp.object", "==");
-    PXOPT_COPY_F32(config->args, where, "-solang_min", "rawExp.solang", ">=");
-    PXOPT_COPY_F32(config->args, where, "-solang_max", "rawExp.solang", "<");
-    PXOPT_COPY_S16(config->args, where, "-code", "warpSkyfile.fault", "==");
+    PXOPT_COPY_STR(config->args, where, "-tess_id",    "warpSkyfile.tess_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-reduction",  "rawExp.reduction", "==");
+    PXOPT_COPY_STR(config->args, where, "-label",      "warpRun.label", "==");
+    PXOPT_COPY_S16(config->args, where, "-code",       "warpSkyfile.fault", "==");
 
     if (!psListLength(where->list)
@@ -1246,4 +1095,8 @@
     int numUpdated;                     // Number updated
     {
+        // This query is no longer necessary because we do not set warpRun's to full statte
+        // if they have faulted skyfiles.
+        // We do have runs in the DB that follow the old convention so we leave this in for
+        // now
         psString query = pxDataGet("warptool_revertwarped_update.sql");
         if (!query) {
@@ -1273,6 +1126,8 @@
         numUpdated = psDBAffectedRows(config->dbh);
 
+#ifdef notdef
+        // new warpRuns won't get changed (they're already new) so don't require an update
         if (numUpdated < 1) {
-            psError(PS_ERR_UNKNOWN, false, "should have affected atleast 1 row");
+            psError(PS_ERR_UNKNOWN, false, "should have affected at least 1 row");
             if (!psDBRollback(config->dbh)) {
                 psError(PS_ERR_UNKNOWN, false, "database error");
@@ -1280,4 +1135,5 @@
             return false;
         }
+#endif
     }
 
@@ -1462,12 +1318,10 @@
     PS_ASSERT_PTR_NON_NULL(config, NULL);
 
-    PXOPT_LOOKUP_S64(warp_id, config->args, "-warp_id", false, false);
+    PXOPT_LOOKUP_S64(warp_id, config->args, "-warp_id", true, false);
     PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
     PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
 
     psMetadata *where = psMetadataAlloc();
-    if (warp_id) {
-        PXOPT_COPY_S64(config->args, where, "-warp_id", "warp_id", "==");
-    }
+    PXOPT_COPY_S64(config->args, where, "-warp_id", "warp_id", "==");
     PXOPT_COPY_STR(config->args, where, "-label", "label", "==");
 
@@ -1624,5 +1478,6 @@
 
     // note only updates if warpRun.state = run_state
-    if (!p_psDBRunQueryF(config->dbh, query, data_state, warp_id, skycell_id, run_state)) {
+    // XXX note that we have removed this constraint for now
+    if (!p_psDBRunQueryF(config->dbh, query, data_state, warp_id, skycell_id)) {
         psError(PS_ERR_UNKNOWN, false, "database error");
         // rollback
Index: /branches/cnb_branches/cnb_branch_20090301/ippTools/src/warptoolConfig.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippTools/src/warptoolConfig.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippTools/src/warptoolConfig.c	(revision 23594)
@@ -51,47 +51,7 @@
     // XXX need to allow multiple exp_ids
     psMetadata *definebyqueryArgs = psMetadataAlloc();
-    psMetadataAddS64(definebyqueryArgs, PS_LIST_TAIL, "-fake_id",            0, "search by fake_id", 0);
-    psMetadataAddS64(definebyqueryArgs, PS_LIST_TAIL, "-cam_id",             0, "search by cam_id", 0);
-    psMetadataAddS64(definebyqueryArgs, PS_LIST_TAIL, "-chip_id",            0, "search by chip_id", 0);
-    psMetadataAddS64(definebyqueryArgs, PS_LIST_TAIL, "-exp_id",             0, "search by exp_id", 0);
-    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-exp_name",           0, "search by exp_name", NULL);
-    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-inst",               0, "search for camera", NULL);
-    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-telescope",          0, "search for telescope", NULL);
-    psMetadataAddTime(definebyqueryArgs, PS_LIST_TAIL, "-dateobs_begin",     0, "search for exposures by time (>=)", NULL);
-    psMetadataAddTime(definebyqueryArgs, PS_LIST_TAIL, "-dateobs_end",       0, "search for exposures by time (<)", NULL);
-    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-exp_tag",            0, "search by exp_tag", NULL);
-    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-exp_type",           0, "search by exp_type", NULL);
-    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-filelevel",          0, "search by filelevel", NULL);
-    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-reduction",          0, "search by reduction class", NULL);
-    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-filter",             0, "search for filter", NULL);
-    psMetadataAddF64(definebyqueryArgs, PS_LIST_TAIL, "-airmass_min",        0, "define min airmass", NAN);
-    psMetadataAddF64(definebyqueryArgs, PS_LIST_TAIL, "-airmass_max",        0, "define max airmass", NAN);
-    psMetadataAddF64(definebyqueryArgs, PS_LIST_TAIL, "-ra_min",             0, "define min", NAN);
-    psMetadataAddF64(definebyqueryArgs, PS_LIST_TAIL, "-ra_max",             0, "define max", NAN);
-    psMetadataAddF64(definebyqueryArgs, PS_LIST_TAIL, "-decl_min",           0, "define min", NAN);
-    psMetadataAddF64(definebyqueryArgs, PS_LIST_TAIL, "-decl_max",           0, "define max", NAN);
-    psMetadataAddF32(definebyqueryArgs, PS_LIST_TAIL, "-exp_time_min",       0, "define min", NAN);
-    psMetadataAddF32(definebyqueryArgs, PS_LIST_TAIL, "-exp_time_max",       0, "define max", NAN);
-    psMetadataAddF32(definebyqueryArgs, PS_LIST_TAIL, "-sat_pixel_frac_min", 0, "define max fraction of saturated pixels", NAN);
-    psMetadataAddF32(definebyqueryArgs, PS_LIST_TAIL, "-sat_pixel_frac_max", 0, "define min fraction of saturated pixels", NAN);
-    psMetadataAddF64(definebyqueryArgs, PS_LIST_TAIL, "-bg_min",             0, "define max", NAN);
-    psMetadataAddF64(definebyqueryArgs, PS_LIST_TAIL, "-bg_max",             0, "define max", NAN);
-    psMetadataAddF64(definebyqueryArgs, PS_LIST_TAIL, "-bg_stdev_min",       0, "define max", NAN);
-    psMetadataAddF64(definebyqueryArgs, PS_LIST_TAIL, "-bg_stdev_max",       0, "define max", NAN);
-    psMetadataAddF64(definebyqueryArgs, PS_LIST_TAIL, "-bg_mean_stdev_min",  0, "define max", NAN);
-    psMetadataAddF64(definebyqueryArgs, PS_LIST_TAIL, "-bg_mean_stdev_max",  0, "define max", NAN);
-    psMetadataAddF64(definebyqueryArgs, PS_LIST_TAIL, "-alt_min",            0, "define min", NAN);
-    psMetadataAddF64(definebyqueryArgs, PS_LIST_TAIL, "-alt_max",            0, "define max", NAN);
-    psMetadataAddF64(definebyqueryArgs, PS_LIST_TAIL, "-az_min",             0, "define min", NAN);
-    psMetadataAddF64(definebyqueryArgs, PS_LIST_TAIL, "-az_max",             0, "define max", NAN);
-    psMetadataAddF32(definebyqueryArgs, PS_LIST_TAIL, "-ccd_temp_min",       0, "define min ccd tempature", NAN);
-    psMetadataAddF32(definebyqueryArgs, PS_LIST_TAIL, "-ccd_temp_max",       0, "define max ccd tempature", NAN);
-    psMetadataAddF64(definebyqueryArgs, PS_LIST_TAIL, "-posang_min",         0, "define min rotator position angle", NAN);
-    psMetadataAddF64(definebyqueryArgs, PS_LIST_TAIL, "-posang_max",         0, "define max rotator position angle", NAN);
-    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-object",             0, "search by exposure object", NULL);
-    psMetadataAddF32(definebyqueryArgs, PS_LIST_TAIL, "-solang_min",         0, "define min solar angle", NAN);
-    psMetadataAddF32(definebyqueryArgs, PS_LIST_TAIL, "-solang_max",         0, "define max solar angle", NAN);
-    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-label", 0, "search on fakeRun label", NULL);
-
+    pxwarpSetSearchArgs (definebyqueryArgs);
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-reduction",          0, "search by fakeRun reduction class", NULL);
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-label",              0, "search on fakeRun label", NULL);
     psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-set_mode",           0, "define mode (warp, diff, stack, magic)", NULL);
     psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-set_workdir",        0, "define workdir", NULL);
@@ -123,54 +83,12 @@
     // XXX need to allow multiple exp_ids
     psMetadata *updaterunArgs = psMetadataAlloc();
-    psMetadataAddS64(updaterunArgs, PS_LIST_TAIL, "-warp_id", 0,            "search by warptool ID", 0);
-    psMetadataAddS64(updaterunArgs, PS_LIST_TAIL, "-fake_id",  0,            "search by fake_id", 0);
-    psMetadataAddS64(updaterunArgs, PS_LIST_TAIL, "-chip_id",  0,            "search by chip_id", 0);
-    psMetadataAddS64(updaterunArgs, PS_LIST_TAIL, "-exp_id",  0,            "search by exp_id", 0);
-    psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-exp_name",  0,            "search by exp_name", NULL);
-    psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-inst",  0,            "search for camera", NULL);
-    psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-telescope",  0,            "search for telescope", NULL);
-    psMetadataAddTime(updaterunArgs, PS_LIST_TAIL, "-dateobs_begin", 0,            "search for exposures by time (>=)", NULL);
-    psMetadataAddTime(updaterunArgs, PS_LIST_TAIL, "-dateobs_end", 0,            "search for exposures by time (<)", NULL);
-    psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-exp_tag",  0,            "search by exp_tag", NULL);
-    psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-exp_type",  0,            "search by exp_type", NULL);
-    psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-filelevel",  0,            "search by filelevel", NULL);
-    psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-reduction",  0,            "search by reduction class", NULL);
-    psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-filter",  0,            "search for filter", NULL);
-    psMetadataAddF32(updaterunArgs, PS_LIST_TAIL, "-airmass_min",  0,            "define min airmass", NAN);
-    psMetadataAddF32(updaterunArgs, PS_LIST_TAIL, "-airmass_max",  0,            "define max airmass", NAN);
-    psMetadataAddF64(updaterunArgs, PS_LIST_TAIL, "-ra_min",  0,            "define min", NAN);
-    psMetadataAddF64(updaterunArgs, PS_LIST_TAIL, "-ra_max",  0,            "define max", NAN);
-    psMetadataAddF64(updaterunArgs, PS_LIST_TAIL, "-decl_min",  0,            "define min", NAN);
-    psMetadataAddF64(updaterunArgs, PS_LIST_TAIL, "-decl_max",  0,            "define max", NAN);
-    psMetadataAddF32(updaterunArgs, PS_LIST_TAIL, "-exp_time_min",  0,            "define min", NAN);
-    psMetadataAddF32(updaterunArgs, PS_LIST_TAIL, "-exp_time_max",  0,            "define max", NAN);
-    psMetadataAddF32(updaterunArgs, PS_LIST_TAIL, "-sat_pixel_frac_min",  0,            "define max fraction of saturated pixels", NAN);
-    psMetadataAddF32(updaterunArgs, PS_LIST_TAIL, "-sat_pixel_frac_max",  0,            "define min fraction of saturated pixels", NAN);
-    psMetadataAddF64(updaterunArgs, PS_LIST_TAIL, "-bg_min",  0,            "define max", NAN);
-    psMetadataAddF64(updaterunArgs, PS_LIST_TAIL, "-bg_max",  0,            "define max", NAN);
-    psMetadataAddF64(updaterunArgs, PS_LIST_TAIL, "-bg_stdev_min",  0,            "define max", NAN);
-    psMetadataAddF64(updaterunArgs, PS_LIST_TAIL, "-bg_stdev_max",  0,            "define max", NAN);
-    psMetadataAddF64(updaterunArgs, PS_LIST_TAIL, "-bg_mean_stdev_min",  0,            "define max", NAN);
-    psMetadataAddF64(updaterunArgs, PS_LIST_TAIL, "-bg_mean_stdev_max",  0,            "define max", NAN);
-    psMetadataAddF64(updaterunArgs, PS_LIST_TAIL, "-alt_min",  0,            "define min", NAN);
-    psMetadataAddF64(updaterunArgs, PS_LIST_TAIL, "-alt_max",  0,            "define max", NAN);
-    psMetadataAddF64(updaterunArgs, PS_LIST_TAIL, "-az_min",  0,            "define min", NAN);
-    psMetadataAddF64(updaterunArgs, PS_LIST_TAIL, "-az_max",  0,            "define max", NAN);
-    psMetadataAddF64(updaterunArgs, PS_LIST_TAIL, "-ccd_temp_min",  0,            "define min ccd tempature", NAN);
-    psMetadataAddF64(updaterunArgs, PS_LIST_TAIL, "-ccd_temp_max",  0,            "define max ccd tempature", NAN);
-    psMetadataAddF64(updaterunArgs, PS_LIST_TAIL, "-posang_min",  0,            "define min rotator position angle", NAN);
-    psMetadataAddF64(updaterunArgs, PS_LIST_TAIL, "-posang_max",  0,            "define max rotator position angle", NAN);
-    psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-object",  0,            "search by exposure object", NULL);
-    psMetadataAddF32(updaterunArgs, PS_LIST_TAIL, "-solang_min",  0,            "define min solar angle", NAN);
-    psMetadataAddF32(updaterunArgs, PS_LIST_TAIL, "-solang_max",  0,            "define max solar angle", NAN);
-
-    psMetadataAddBool(updaterunArgs, PS_LIST_TAIL, "-all",  0,            "allow everything to be queued without search terms", false);
-    psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-state", 0,            "set state", NULL);
-    psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-label", 0,            "set label", NULL);
-
-#if 0
-    psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-workdir", 0,            "define workdir (required)", NULL);
-    psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-registered",  0,            "time detrend run was registered", now);
-#endif
+    pxwarpSetSearchArgs (updaterunArgs);
+    psMetadataAddS64(updaterunArgs, PS_LIST_TAIL, "-warp_id", 0,    "search by warptool ID", 0);
+    psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-reduction", 0,  "search by warpRun reduction class", NULL);
+    psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-state", 0,      "search by warpRun state", NULL);
+    psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-label", 0,      "search by warpRun label", NULL);
+    psMetadataAddBool(updaterunArgs, PS_LIST_TAIL, "-all",  0,      "allow everything to be queued without search terms", false);
+    psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-set_state", 0,  "set state", NULL);
+    psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-set_label", 0,  "set label", NULL);
 
     // -exp
@@ -235,4 +153,5 @@
     psMetadataAddF32(addwarpedArgs, PS_LIST_TAIL, "-good_frac",  0,            "define %% of good pixels", NAN);
     psMetadataAddBool(addwarpedArgs, PS_LIST_TAIL, "-accept",  0, "define if this skycell should be accepted", false);
+    psMetadataAddBool(addwarpedArgs, PS_LIST_TAIL, "-magicked",  0, "define if this skycell has been magicked", false);
     psMetadataAddS16(addwarpedArgs, PS_LIST_TAIL, "-code",  0,            "set fault code", 0);
 
@@ -254,49 +173,10 @@
     // XXX need to allow multiple exp_ids
     psMetadata *revertwarpedArgs = psMetadataAlloc();
+    pxwarpSetSearchArgs(revertwarpedArgs); // XXX does this work here?
     psMetadataAddS64(revertwarpedArgs, PS_LIST_TAIL, "-warp_id", 0,            "search by warptool ID", 0);
     psMetadataAddStr(revertwarpedArgs, PS_LIST_TAIL, "-skycell_id",  0,            "search by skycell ID", NULL);
-    psMetadataAddStr(revertwarpedArgs, PS_LIST_TAIL, "-tess_id",  0,            "searcy by tessellation ID", NULL);
-    psMetadataAddS64(revertwarpedArgs, PS_LIST_TAIL, "-fake_id",  0,            "search by fake_id", 0);
-    psMetadataAddS64(revertwarpedArgs, PS_LIST_TAIL, "-chip_id",  0,            "search by chip_id", 0);
-    psMetadataAddS64(revertwarpedArgs, PS_LIST_TAIL, "-exp_id",  0,            "search by exp_id", 0);
-    psMetadataAddStr(revertwarpedArgs, PS_LIST_TAIL, "-exp_name",  0,            "search by exp_name", NULL);
-    psMetadataAddStr(revertwarpedArgs, PS_LIST_TAIL, "-label",  0,            "search by label", NULL);
-    psMetadataAddStr(revertwarpedArgs, PS_LIST_TAIL, "-inst",  0,            "search for camera", NULL);
-    psMetadataAddStr(revertwarpedArgs, PS_LIST_TAIL, "-telescope",  0,            "search for telescope", NULL);
-    psMetadataAddTime(revertwarpedArgs, PS_LIST_TAIL, "-dateobs_begin", 0,            "search for exposures by time (>=)", NULL);
-    psMetadataAddTime(revertwarpedArgs, PS_LIST_TAIL, "-dateobs_end", 0,            "search for exposures by time (<)", NULL);
-    psMetadataAddStr(revertwarpedArgs, PS_LIST_TAIL, "-exp_tag",  0,            "search by exp_tag", NULL);
-    psMetadataAddStr(revertwarpedArgs, PS_LIST_TAIL, "-exp_type",  0,            "search by exp_type", NULL);
-    psMetadataAddStr(revertwarpedArgs, PS_LIST_TAIL, "-filelevel",  0,            "search by filelevel", NULL);
-    psMetadataAddStr(revertwarpedArgs, PS_LIST_TAIL, "-reduction",  0,            "search by reduction class", NULL);
-    psMetadataAddStr(revertwarpedArgs, PS_LIST_TAIL, "-filter",  0,            "search for filter", NULL);
-    psMetadataAddF32(revertwarpedArgs, PS_LIST_TAIL, "-airmass_min",  0,            "define min airmass", NAN);
-    psMetadataAddF32(revertwarpedArgs, PS_LIST_TAIL, "-airmass_max",  0,            "define max airmass", NAN);
-    psMetadataAddF64(revertwarpedArgs, PS_LIST_TAIL, "-ra_min",  0,            "define min", NAN);
-    psMetadataAddF64(revertwarpedArgs, PS_LIST_TAIL, "-ra_max",  0,            "define max", NAN);
-    psMetadataAddF64(revertwarpedArgs, PS_LIST_TAIL, "-decl_min",  0,            "define min", NAN);
-    psMetadataAddF64(revertwarpedArgs, PS_LIST_TAIL, "-decl_max",  0,            "define max", NAN);
-    psMetadataAddF32(revertwarpedArgs, PS_LIST_TAIL, "-exp_time_min",  0,            "define min", NAN);
-    psMetadataAddF32(revertwarpedArgs, PS_LIST_TAIL, "-exp_time_max",  0,            "define max", NAN);
-    psMetadataAddF32(revertwarpedArgs, PS_LIST_TAIL, "-sat_pixel_frac_min",  0,            "define max fraction of saturated pixels", NAN);
-    psMetadataAddF32(revertwarpedArgs, PS_LIST_TAIL, "-sat_pixel_frac_max",  0,            "define min fraction of saturated pixels", NAN);
-    psMetadataAddF64(revertwarpedArgs, PS_LIST_TAIL, "-bg_min",  0,            "define max", NAN);
-    psMetadataAddF64(revertwarpedArgs, PS_LIST_TAIL, "-bg_max",  0,            "define max", NAN);
-    psMetadataAddF64(revertwarpedArgs, PS_LIST_TAIL, "-bg_stdev_min",  0,            "define max", NAN);
-    psMetadataAddF64(revertwarpedArgs, PS_LIST_TAIL, "-bg_stdev_max",  0,            "define max", NAN);
-    psMetadataAddF64(revertwarpedArgs, PS_LIST_TAIL, "-bg_mean_stdev_min",  0,            "define max", NAN);
-    psMetadataAddF64(revertwarpedArgs, PS_LIST_TAIL, "-bg_mean_stdev_max",  0,            "define max", NAN);
-    psMetadataAddF64(revertwarpedArgs, PS_LIST_TAIL, "-alt_min",  0,            "define min", NAN);
-    psMetadataAddF64(revertwarpedArgs, PS_LIST_TAIL, "-alt_max",  0,            "define max", NAN);
-    psMetadataAddF64(revertwarpedArgs, PS_LIST_TAIL, "-az_min",  0,            "define min", NAN);
-    psMetadataAddF64(revertwarpedArgs, PS_LIST_TAIL, "-az_max",  0,            "define max", NAN);
-    psMetadataAddF64(revertwarpedArgs, PS_LIST_TAIL, "-ccd_temp_min",  0,            "define min ccd tempature", NAN);
-    psMetadataAddF64(revertwarpedArgs, PS_LIST_TAIL, "-ccd_temp_max",  0,            "define max ccd tempature", NAN);
-    psMetadataAddF64(revertwarpedArgs, PS_LIST_TAIL, "-posang_min",  0,            "define min rotator position angle", NAN);
-    psMetadataAddF64(revertwarpedArgs, PS_LIST_TAIL, "-posang_max",  0,            "define max rotator position angle", NAN);
-    psMetadataAddStr(revertwarpedArgs, PS_LIST_TAIL, "-object",  0,            "search by exposure object", NULL);
-    psMetadataAddF32(revertwarpedArgs, PS_LIST_TAIL, "-solang_min",  0,            "define min solar angle", NAN);
-    psMetadataAddF32(revertwarpedArgs, PS_LIST_TAIL, "-solang_max",  0,            "define max solar angle", NAN);
-
+    psMetadataAddStr(revertwarpedArgs, PS_LIST_TAIL, "-tess_id",  0,            "search by tessellation ID", NULL);
+    psMetadataAddStr(revertwarpedArgs, PS_LIST_TAIL, "-reduction",  0,            "search by warpRun reduction class", NULL);
+    psMetadataAddStr(revertwarpedArgs, PS_LIST_TAIL, "-label",  0,            "search by warpRun label", NULL);
     psMetadataAddS16(revertwarpedArgs, PS_LIST_TAIL, "-code",  0,            "search by fault code", 0);
     psMetadataAddBool(revertwarpedArgs, PS_LIST_TAIL, "-all",  0,            "allow everything to be queued without search terms", false);
@@ -323,5 +203,5 @@
     psMetadata *pendingcleanupskyfileArgs = psMetadataAlloc();
     psMetadataAddStr(pendingcleanupskyfileArgs, PS_LIST_TAIL, "-label",  0,            "list blocks for specified label", NULL);
-    psMetadataAddS64(pendingcleanupskyfileArgs, PS_LIST_TAIL, "-warp_id", 0,          "search by warp ID", 0);
+    psMetadataAddS64(pendingcleanupskyfileArgs, PS_LIST_TAIL, "-warp_id", 0,          "search by warp ID (required)", 0);
     psMetadataAddBool(pendingcleanupskyfileArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
     psMetadataAddU64(pendingcleanupskyfileArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
Index: /branches/cnb_branches/cnb_branch_20090301/ippconfig/gpc1/ppImage.config
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippconfig/gpc1/ppImage.config	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippconfig/gpc1/ppImage.config	(revision 23594)
@@ -14,4 +14,7 @@
 
 OLDDARK                 BOOL    FALSE
+
+# use the deburned image instead of the raw, if it exists
+USE.DEBURNED.IMAGE      BOOL    TRUE           # use burntool-repaired image?
 
 ## XXX use these local variations until we are building real detrend images
Index: /branches/cnb_branches/cnb_branch_20090301/ippconfig/recipes/Makefile.am
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippconfig/recipes/Makefile.am	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippconfig/recipes/Makefile.am	(revision 23594)
@@ -9,4 +9,5 @@
 	addstar.config \
 	masks.config \
+	masks.16bit.config \
 	reductionClasses.mdc \
 	rejections.config \
Index: /branches/cnb_branches/cnb_branch_20090301/ippconfig/recipes/filerules-mef.mdc
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippconfig/recipes/filerules-mef.mdc	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippconfig/recipes/filerules-mef.mdc	(revision 23594)
@@ -1,4 +1,6 @@
 ### File rules for PHU=FPA, EXT=CHIP (eg, Megacam)
 ### this file specifies compression types for various output files
+### The image types are defined in fitstypes.mdc
+### The default type of NONE means no compression
 
 ### Redirections (MEF)
@@ -84,6 +86,11 @@
 PSASTRO.INPUT.CMP       INPUT    @FILES        CHIP       CMP
 PSASTRO.INPUT.CMF       INPUT    @FILES        CHIP       CMF
+PSASTRO.INPUT.MASK      INPUT    @FILES        CHIP       MASK
+PSASTRO.REFMASK         INPUT    @DETDB        CHIP       MASK
 PSASTRO.WCS             INPUT    @FILES        CHIP       CMF
 PSASTRO.MODEL           INPUT    @DETDB        FPA        ASTROM
+
+PSASTRO.EXTRACT.ASTROM  INPUT    @FILES        CHIP       CMF
+PSASTRO.EXTRACT.INPUT   INPUT    @FILES        CHIP       IMAGE
 
 ## files used by pswarp
@@ -229,4 +236,5 @@
 PPSTACK.OUTPUT.VARIANCE OUTPUT {OUTPUT}.wt.fits                  VARIANCE  COMP_WT    FPA        TRUE      NONE
 PPSTACK.TARGET.PSF      OUTPUT {OUTPUT}.target.psf               PSF       NONE       CHIP       TRUE      NONE
+PPSTACK.CONV.KERNEL     OUTPUT {OUTPUT}.{FILE.INDEX}.kernel      SUBKERNEL NONE       FPA        TRUE      NONE
 PPSTACK.OUTPUT.JPEG1    OUTPUT {OUTPUT}.b1.jpg                   JPEG      NONE       FPA        TRUE      NONE
 PPSTACK.OUTPUT.JPEG2    OUTPUT {OUTPUT}.b2.jpg                   JPEG      NONE       FPA        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 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippconfig/recipes/filerules-simple.mdc	(revision 23594)
@@ -1,3 +1,5 @@
 ### File rules for PHU=FPA, EXT=NONE
+### The default type of NONE means no compression
+
 
 PSASTRO.INPUT      STR PSASTRO.INPUT.CMF
@@ -18,5 +20,5 @@
 PPIMAGE.INPUT.PSF       INPUT    @FILES        READOUT    PSF       
 PPIMAGE.INPUT.SRC       INPUT    @FILES        READOUT    CMF       
-PPIMAGE.MASK            INPUT    @DETDB        CHIP       IMAGE     
+PPIMAGE.MASK            INPUT    @DETDB        CHIP       MASK
 PPIMAGE.BIAS            INPUT    @DETDB        CHIP       IMAGE     
 PPIMAGE.DARK            INPUT    @DETDB        CHIP       DARK     
@@ -47,6 +49,11 @@
 PSASTRO.INPUT.CMP       INPUT    @FILES        CHIP       CMP       
 PSASTRO.INPUT.CMF       INPUT    @FILES        CHIP       CMF       
+PSASTRO.INPUT.MASK      INPUT    @FILES        CHIP       MASK
+PSASTRO.REFMASK         INPUT    @DETDB        CHIP       MASK
 PSASTRO.WCS             INPUT    @FILES        CHIP       CMF
 PSASTRO.MODEL           INPUT    @DETDB        FPA        ASTROM
+
+PSASTRO.EXTRACT.ASTROM  INPUT    @FILES        CHIP       CMF
+PSASTRO.EXTRACT.INPUT   INPUT    @FILES        CHIP       IMAGE
 
 ## files used by pswarp
@@ -181,4 +188,5 @@
 PPSTACK.OUTPUT.VARIANCE OUTPUT {OUTPUT}.weight.fits  VARIANCE  NONE       FPA        TRUE      NONE
 PPSTACK.TARGET.PSF      OUTPUT {OUTPUT}.target.psf   PSF       NONE       CHIP       TRUE      NONE
+PPSTACK.CONV.KERNEL     OUTPUT {OUTPUT}.{FILE.INDEX}.kernel SUBKERNEL NONE FPA       TRUE      NONE
 PPSTACK.OUTPUT.JPEG1    OUTPUT {OUTPUT}.b1.jpg       JPEG      NONE       FPA        TRUE      NONE
 PPSTACK.OUTPUT.JPEG2    OUTPUT {OUTPUT}.b2.jpg       JPEG      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 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippconfig/recipes/filerules-split.mdc	(revision 23594)
@@ -1,4 +1,5 @@
 ### File rules for PHU=CHIP, EXT=CELL (eg, GPC1)
 ### this file specifies compression types for various output files
+### The default type of NONE means no compression
 
 # for basic chip analysis, psphot should produce SPLIT output
@@ -61,4 +62,7 @@
 PSASTRO.WCS             INPUT    @FILES        CHIP       WCS
 PSASTRO.MODEL           INPUT    @DETDB        FPA        ASTROM
+
+PSASTRO.EXTRACT.ASTROM  INPUT    @FILES        CHIP       CMF
+PSASTRO.EXTRACT.INPUT   INPUT    @FILES        CHIP       IMAGE
 
 ## files used by pswarp
@@ -119,5 +123,5 @@
 PPIMAGE.JPEG1           OUTPUT {OUTPUT}.b1.jpg                   JPEG      NONE       FPA        TRUE      NONE
 PPIMAGE.JPEG2           OUTPUT {OUTPUT}.b2.jpg                   JPEG      NONE       FPA        TRUE      NONE
-		        									        
+
 PPIMAGE.STATS           OUTPUT {OUTPUT}.{CHIP.NAME}.stats        STATS     NONE       CHIP       TRUE      NONE
 
@@ -197,4 +201,5 @@
 PPSTACK.OUTPUT.VARIANCE OUTPUT {OUTPUT}.wt.fits                  VARIANCE  COMP_WT    FPA        TRUE      NONE
 PPSTACK.TARGET.PSF      OUTPUT {OUTPUT}.target.psf               PSF       NONE       CHIP       TRUE      NONE
+PPSTACK.CONV.KERNEL     OUTPUT {OUTPUT}.{FILE.INDEX}.kernel      SUBKERNEL NONE       FPA        TRUE      NONE
 PPSTACK.OUTPUT.JPEG1	OUTPUT {OUTPUT}.b1.jpg                   JPEG      NONE       FPA        TRUE      NONE
 PPSTACK.OUTPUT.JPEG2	OUTPUT {OUTPUT}.b2.jpg                   JPEG      NONE       FPA        TRUE      NONE
Index: /branches/cnb_branches/cnb_branch_20090301/ippconfig/recipes/jpeg.mdc
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippconfig/recipes/jpeg.mdc	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippconfig/recipes/jpeg.mdc	(revision 23594)
@@ -182,2 +182,18 @@
 	END
 END
+
+CTEMAP	METADATA
+	PPIMAGE.JPEG1		 METADATA
+		COLORMAP	STR	rainbow
+		SCALE.MODE	STR	VALUE
+		SCALE.MIN	F32	0.0
+		SCALE.MAX	F32	5.0
+	END
+
+	PPIMAGE.JPEG2		 METADATA
+		COLORMAP	STR	rainbow
+		SCALE.MODE	STR	VALUE
+		SCALE.MIN	F32	0.0
+		SCALE.MAX	F32	5.0
+	END
+END
Index: /branches/cnb_branches/cnb_branch_20090301/ippconfig/recipes/masks.16bit.config
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippconfig/recipes/masks.16bit.config	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippconfig/recipes/masks.16bit.config	(revision 23594)
@@ -8,21 +8,26 @@
 
 # Mask values which represent features of the detector
-DETECTOR	U8	0x0001		# Something is wrong with the detector
-FLAT		U8	0x0002		# Pixel doesn't flat-field properly
-DARK		U8	0x0004		# Pixel doesn't dark-subtract properly
-BLANK		U8	0x0008		# Pixel doesn't contain valid data
+DETECTOR	U16	0x0001		# Something is wrong with the detector
+FLAT		U16	0x0002		# Pixel doesn't flat-field properly
+DARK		U16	0x0004		# Pixel doesn't dark-subtract properly
+BLANK		U16	0x0008		# Pixel doesn't contain valid data
+CTE		U16	0x0010		# Pixel has poor Charge Transfer Efficiency
 
 # Mask values which represent invalid signal ranges
-RANGE		U8	0x0010		# Pixel is out-of-range of linearity
-SAT		U8	0x0020		# Pixel is saturated
-BAD		U8	0x0040		# Pixel is low (can we rename this 'LOW'?)
+SAT		U16	0x0020		# Pixel is saturated or non-linear
+LOW		U16	0x0040		# Pixel is low
+SUSPECT		U16	0x0080		# Pixel is suspected of being bad
 
 # Mask values which represent non-astronomical structures
-GHOST		U8	0x0100		# Pixel contains an optical ghost
-CR		U8	0x0200		# Pixel contains a cosmic ray
-STREAK		U8	0x0400		# Pixel contains a streak
+CR		U16	0x0100		# Pixel contains a cosmic ray
+SPIKE		U16	0x0200		# Pixel contains a diffraction spike
+GHOST		U16	0x0400		# Pixel contains an optical ghost
+STREAK		U16	0x0800		# Pixel contains a streak
+STARCORE	U16	0x1000		# Pixel contains a bright star core
 
 # Mask values which identify pixels badly affected by convolutions and interpolations
-BAD.WARP	U8	0x1000		# Pixel is bad after convolution with a bad pixel
-POOR.WARP	U8	0x2000		# Pixel is poor after convolution with a bad pixel
+CONV.BAD	U16	0x2000		# Pixel is bad after convolution with a bad pixel
+CONV.POOR	U16	0x4000		# Pixel is poor after convolution with a bad pixel
 
+# Spare value for temporary marking
+# MARK		U16	0x8000		# Pixel is temporarily marked
Index: /branches/cnb_branches/cnb_branch_20090301/ippconfig/recipes/masks.config
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippconfig/recipes/masks.config	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippconfig/recipes/masks.config	(revision 23594)
@@ -14,4 +14,6 @@
 FLAT		U8	0x04		# Pixel doesn't flat-field properly =4
 DARK		U8	0x40		# Pixel doesn't dark-subtract properly =64
+CTE		U8	0x40		# Pixel has poor cte
+
 # Filling in the rest with whatever makes some sense
 BLANK		U8	0x01		# Pixel doesn't contain valid data
@@ -24,4 +26,7 @@
 GHOST		U8	0x20		# Pixel contains an optical ghost
 STREAK		U8	0x20		# Pixel contains a streak
+
+SPIKE		U8	0x20		# Pixel contains a diffraction spike
+STARCORE	U8	0x20		# Pixel contains a streak
 
 ###### The following values are what I'm aiming to have in the long term (PAP, 2008-09-09)
Index: /branches/cnb_branches/cnb_branch_20090301/ippconfig/recipes/ppImage.config
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippconfig/recipes/ppImage.config	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippconfig/recipes/ppImage.config	(revision 23594)
@@ -19,4 +19,6 @@
 ASTROM.MOSAIC      BOOL    FALSE           # Astrometry for mosaic?
 BACKGROUND         BOOL    FALSE           # Subtract model background?
+CHECK.CTE          BOOL    FALSE           # measure CTE errors?
+USE.DEBURNED.IMAGE BOOL    FALSE           # use burntool-repaired image?
 
 # output data formats to save
@@ -77,4 +79,7 @@
 BIN2.XBIN               S32     16
 BIN2.YBIN               S32     16
+
+CTE.XBIN                S32     20
+CTE.YBIN                S32     20
 
 PPIMAGE.JPEG1  METADATA
@@ -470,8 +475,8 @@
   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? 
+  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
@@ -1417,4 +1422,28 @@
 END
 
+# generate CTE map image
+PPIMAGE_CTEMAP     METADATA
+  BASE.FITS        BOOL    TRUE            # Save base 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    FALSE           # Overscan subtraction
+  OVERSCAN         BOOL    TRUE            # Overscan subtraction
+  BIAS             BOOL    TRUE            # Bias subtraction
+  DARK             BOOL    TRUE            # Dark subtraction
+  SHUTTER          BOOL    FALSE           # Shutter correction
+  FLAT             BOOL    TRUE            # Flat-field normalisation
+  MASK             BOOL    FALSE           # Mask bad pixels
+  FRINGE           BOOL    FALSE           # Fringe subtraction
+  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?
+  CHECK.CTE        BOOL    TRUE            # measure CTE errors?
+END
+
 # For SDSS cameras
 PPIMAGE_OA_SOFT   METADATA
Index: /branches/cnb_branches/cnb_branch_20090301/ippconfig/recipes/ppMerge.config
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippconfig/recipes/ppMerge.config	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippconfig/recipes/ppMerge.config	(revision 23594)
@@ -14,4 +14,5 @@
 FRINGE.XSMOOTH	S32	5		# Number of smoothing regions in x
 FRINGE.YSMOOTH	S32	11		# Number of smoothing regions in y
+CTE.MIN         F32	0.2		# regions lower than this in the CTE image are masked
 SHUTTER.SIZE	S32	128		# Size for shutter measurement regions
 MASK.SUSPECT	F32	5.0		# Threshold for suspect pixels (sigma)
@@ -94,4 +95,17 @@
 END
 
+# CTEMASK generation combines the images like a flat or bias (mean after outlier rejection),
+# but it then sets a mask based on the resulting data values (using CTE.MIN)
+PPMERGE_CTEMASK METADATA
+	REJ		F32	3.0		# Rejection threshold (sigma)
+	ITER		S32	2		# Number of rejection iterations
+	FRACHIGH	F32	0.0		# Fraction of high pixels to reject immediately
+	FRACLOW		F32	0.0		# Fraction of low pixels to reject immediately
+	VARIANCES	BOOL	FALSE		# Use image variances?
+	COMBINE		STR	CLIPPED		# Statistic to use for combination: 
+        CTE.MIN         F32	0.2		# regions lower than this in the CTE image are masked
+        MASK.SET.VALUE	STR	CTE		# set this bit in the output mask
+END
+
 # Shutter generation --- already included in default, above
 PPMERGE_SHUTTER	METADATA
Index: /branches/cnb_branches/cnb_branch_20090301/ippconfig/recipes/ppSim.config
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippconfig/recipes/ppSim.config	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippconfig/recipes/ppSim.config	(revision 23594)
@@ -66,4 +66,9 @@
 
 GALAXY.MODEL     STR    PS_MODEL_GAUSS
+
+BADCTE          BOOL    FALSE           # create a region of 'bad CTE'?
+BADCTE.REGION   STR     [0:200,0:200]   # region of 'bad CTE'
+BADCTE.SIGMA    F32     2.0             # 'bad CTE' smoothing scale
+BADCTE.NSIGMA   S32     2               # 'bad CTE' smoothing scale
 
 BADPIX.SEED	U64	123456789	# Seed for RNG in creating deterministic bad pixels
@@ -185,2 +190,7 @@
     BADPIX.MODE	STR	NAN		# Mode for bad pixels: RANDOM|NAN
 END
+
+# recipe for a CTE test
+BADCTE.TEST     METADATA
+  BADCTE BOOL TRUE
+END
Index: /branches/cnb_branches/cnb_branch_20090301/ippconfig/recipes/ppStack.config
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippconfig/recipes/ppStack.config	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippconfig/recipes/ppStack.config	(revision 23594)
@@ -1,11 +1,12 @@
 # Recipe configuration for ppStack (image combination)
 
+CONVOLVE	BOOL	TRUE		# Convolve images when stacking?
 ITER		S32	1		# Number of rejection iterations
 COMBINE.REJ	F32	3.0		# Rejection threshold in combination (sigma)
 COMBINE.SYS	F32	0.08		# Relative systematic error in combination
 COMBINE.DISCARD	F32	0.2		# Discard fraction for Olympic weighted mean
-MASK.VAL	STR	MASK.VALUE,BAD.WARP	# Mask value of input bad pixels
+MASK.VAL	STR	MASK.VALUE,CONV.BAD	# Mask value of input bad pixels
 MASK.BAD	STR	BLANK		# Mask value to give bad pixels
-MASK.POOR	STR	POOR.WARP	# Mask value to give poor pixels
+MASK.POOR	STR	CONV.POOR	# Mask value to give poor pixels
 POOR.FRACTION	F32	0.01		# Maximum fraction of bad variance for poor pixels
 THRESHOLD.MASK	F32	0.5		# Threshold for mask deconvolution (0..1)
Index: /branches/cnb_branches/cnb_branch_20090301/ippconfig/recipes/ppSub.config
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippconfig/recipes/ppSub.config	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippconfig/recipes/ppSub.config	(revision 23594)
@@ -15,7 +15,7 @@
 
 # EAM : we now ignore these: BAD.WARP is always bad, POOR.WARP is always defined
-MASK.IN		STR	MASK.VALUE,BAD.WARP	# Mask value for input
-MASK.BAD        STR	BLANK		# Mask value to give bad pixels
-MASK.POOR       STR	POOR.WARP	# Mask value to give poor pixels
+MASK.IN		STR	MASK.VALUE,CONV.BAD	# Mask value for input
+MASK.BAD        STR	CONV.BAD	# Mask value to give bad pixels
+MASK.POOR       STR	CONV.POOR	# Mask value to give poor pixels
 
 POOR.FRACTION	F32	0.10		# Maximum fraction of bad weight for poor pixels
Index: /branches/cnb_branches/cnb_branch_20090301/ippconfig/recipes/psastro.config
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippconfig/recipes/psastro.config	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippconfig/recipes/psastro.config	(revision 23594)
@@ -124,4 +124,6 @@
 REFSTAR_MASK_BLEED_MAG_SLOPE    F32   5.0
 
+EXTRACT_MAX_MAG                 F32 -15.0
+
 # 2MASS default configuration (use 2MASS_J as ref magnitude)
 PSASTRO.CATDIR                STR      2MASS
Index: /branches/cnb_branches/cnb_branch_20090301/ippconfig/recipes/reductionClasses.mdc
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippconfig/recipes/reductionClasses.mdc	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippconfig/recipes/reductionClasses.mdc	(revision 23594)
@@ -111,4 +111,11 @@
 	FLATMASK_JPEG_RESID	STR	FLAT_RESID
 
+	CTEMASK_PROCESS		STR	PPIMAGE_CTEMAP
+	CTEMASK_RESID		STR	PPIMAGE_M
+	CTEMASK_VERIFY		STR	PPIMAGE_M
+	CTEMASK_STACK		STR	PPMERGE_CTEMASK
+	CTEMASK_JPEG_IMAGE	STR	CTEMAP
+	CTEMASK_JPEG_RESID	STR	CTEMAP
+
 	FRINGE_PROCESS		STR	PPIMAGE_OBDSF
 	FRINGE_RESID		STR	PPIMAGE_R
Index: /branches/cnb_branches/cnb_branch_20090301/ippconfig/recipes/rejections.config
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippconfig/recipes/rejections.config	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippconfig/recipes/rejections.config	(revision 23594)
@@ -361,4 +361,28 @@
 
 FLATMASK METADATA
+  FILTER      	     STR  *
+  EXPECTED    	     F32  0.0
+  IMFILE.MEAN 	     F32  0.0
+  IMFILE.STDEV       F32  0.0
+  IMFILE.MEANSTDEV   F32  0.0
+  IMFILE.SKEWNESS    F32  0.0
+  IMFILE.KURTOSIS    F32  0.0
+  IMFILE.SNR         F32  0.0
+  IMFILE.BIN.STDEV   F32  0.0
+  IMFILE.BIN.SNR     F32  0.0
+  IMFILE.FLUX        F32  0.0
+  EXP.MEAN           F32  0.0
+  EXP.STDEV          F32  0.0
+  EXP.MEANSTDEV      F32  0.0
+  EXP.SNR            F32  0.0
+  EXP.BIN.STDEV      F32  0.0
+  EXP.BIN.SNR        F32  0.0
+  EXP.FLUX           F32  0.0
+  ENSEMBLE.MEAN      F32  0.0
+  ENSEMBLE.STDEV     F32  0.0
+  ENSEMBLE.MEANSTDEV F32  0.0
+END
+
+CTEMASK METADATA
   FILTER      	     STR  *
   EXPECTED    	     F32  0.0
Index: /branches/cnb_branches/cnb_branch_20090301/ippconfig/system.config
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ippconfig/system.config	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ippconfig/system.config	(revision 23594)
@@ -36,5 +36,5 @@
 
 RECIPES		METADATA		# Site-level recipes
-	MASKS		STR		recipes/masks.config	# Mask values
+	MASKS		STR		recipes/masks.16bit.config	# Mask values
 	REJECTIONS	STR		recipes/rejections.config # Rejection for detrend creation
 	PPIMAGE		STR		recipes/ppImage.config  # Image reduction
Index: /branches/cnb_branches/cnb_branch_20090301/operations/registration/input
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/operations/registration/input	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/operations/registration/input	(revision 23594)
@@ -66,5 +66,5 @@
     controller host add ipp007 -threads $1
 # ipp008 is flaky
-    controller host add ipp009 -threads $1
+# ipp009 is registration server
     controller host add ipp010 -threads $1
     controller host add ipp011 -threads $1
@@ -104,5 +104,5 @@
     controller host add ipp007
 # ipp008 is flaky
-    controller host add ipp009
+# ipp009 is registration server
     controller host add ipp010
     controller host add ipp011
Index: /branches/cnb_branches/cnb_branch_20090301/operations/registration/ptolemy.rc
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/operations/registration/ptolemy.rc	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/operations/registration/ptolemy.rc	(revision 23594)
@@ -2,5 +2,5 @@
 PANTASKS_SERVER_STDOUT  pantasks.stdout.log
 PANTASKS_SERVER_STDERR  pantasks.stderr.log
-PANTASKS_SERVER         ipp008
+PANTASKS_SERVER         ipp009
 PASSWORD                foobar
 
Index: /branches/cnb_branches/cnb_branch_20090301/ppImage/src/ppImageCheckCTE.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppImage/src/ppImageCheckCTE.c	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/ppImage/src/ppImageCheckCTE.c	(revision 23594)
@@ -0,0 +1,106 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "ppImage.h"
+
+// XXX I originally coded this to create a new pmFPAfile, but in retrospect it makes more sense
+// to treat this function as an operation on the input image
+
+// XXX make the choice of stats optional
+bool ppImageCheckCTE(pmConfig *config, ppImageOptions *options, pmFPAview *view)
+{
+    bool status;
+
+    // this step is complete optional.
+    if (!options->checkCTE) {
+        return true;
+    }
+
+    // add recipe options supplied on command line
+    psMetadata *recipe  = psMetadataLookupPtr(&status, config->recipes, RECIPE_NAME);
+
+    // find the currently selected readout
+    pmReadout *inReadout = pmFPAfileThisReadout(config->files, view, "PPIMAGE.INPUT");
+
+    psImage *image    = inReadout->image;
+    psImage *mask     = inReadout->mask;
+    // psImage *variance = inReadout->variance;
+
+    // I have the fine image size, I know the binning factor, determine the ruff image size
+    psImageBinning *binning = psImageBinningAlloc();
+    binning->nXfine = image->numCols;
+    binning->nYfine = image->numRows;
+    binning->nXbin  = psMetadataLookupS32 (&status, recipe, "CTE.XBIN");
+    binning->nYbin  = psMetadataLookupS32 (&status, recipe, "CTE.YBIN");
+
+    psImageBinningSetRuffSize(binning, PS_IMAGE_BINNING_CENTER);
+    psImageBinningSetSkip(binning, image);
+
+    psStats *stats = psStatsAlloc (PS_STAT_ROBUST_MEDIAN);
+    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS);
+    psImageBackground (stats, NULL, image, mask, 0xffff, rng);
+    float cellMedian = stats->robustMedian;
+    psFree (stats);
+
+    // stats = psStatsAlloc (PS_STAT_SAMPLE_STDEV);
+    // psStats *statsDefaults = psStatsAlloc (PS_STAT_SAMPLE_STDEV);
+
+    stats = psStatsAlloc (PS_STAT_ROBUST_STDEV);
+    psStats *statsDefaults = psStatsAlloc (PS_STAT_ROBUST_STDEV);
+
+    // measure median and variance for subimages
+    psRegion ruffRegion = {0,0,0,0};
+    psRegion fineRegion = {0,0,0,0};
+    for (int iy = 0; iy < binning->nYruff; iy++) {
+        for (int ix = 0; ix < binning->nXruff; ix++) {
+
+            // convert the ruff grid cell to the equivalent fine grid cell
+            ruffRegion = psRegionSet (ix, ix + 1, iy, iy + 1);
+            fineRegion = psImageBinningSetFineRegion (binning, ruffRegion);
+            fineRegion = psRegionForImage (image, fineRegion);
+            if (fineRegion.x0 >= image->numCols || fineRegion.x1 >= image->numCols ||
+                fineRegion.y0 >= image->numRows || fineRegion.y1 >= image->numRows) {
+                continue;
+            }
+
+            psImage *subset  = psImageSubset (image, fineRegion);
+            if (!subset->numCols || !subset->numRows) {
+                psFree (subset);
+                continue;
+            }
+            psImage *submask = NULL;
+            if (mask) {
+                submask = psImageSubset (mask, fineRegion);
+            }
+
+            // reset the default values
+            statsDefaults->tmpData = stats->tmpData; // XXX this is fairly hackish: tmpData is internal storage for stats; the assign drops the reference...
+            *stats = *statsDefaults;
+            statsDefaults->tmpData = NULL;
+
+            psImageStats (stats, subset, submask, 0xffff);
+
+	    // XXX need to apply the gain as well
+	    float normVariance = PS_SQR(stats->robustStdev) / cellMedian;
+	    // float normVariance = PS_SQR(stats->sampleStdev) / cellMedian;
+
+	    // apply resulting value to the input pixels
+	    for (int jy = fineRegion.y0; jy < fineRegion.y1; jy++) {
+	      for (int jx = fineRegion.x0; jx < fineRegion.x1; jx++) {
+		image->data.F32[jy][jx] = normVariance;
+	      }
+	    }
+
+            psFree (subset);
+            psFree (submask);
+        }
+    }
+
+    psFree (rng);
+    psFree (binning);
+    psFree (stats);
+    psFree (statsDefaults);
+
+    return true;
+}
Index: /branches/cnb_branches/cnb_branch_20090301/ppImage/src/ppImageCheckCTE.v1.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppImage/src/ppImageCheckCTE.v1.c	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/ppImage/src/ppImageCheckCTE.v1.c	(revision 23594)
@@ -0,0 +1,101 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "ppImage.h"
+
+bool ppImageCheckCTE(pmConfig *config, ppImageOptions *options, pmFPAview *view)
+{
+    bool status;
+
+    // this step is complete optional.
+    if (!options->checkCTE) {
+        return true;
+    }
+
+    // add recipe options supplied on command line
+    psMetadata *recipe  = psMetadataLookupPtr(&status, config->recipes, RECIPE_NAME);
+
+    // find the currently selected readout
+    pmReadout *inReadout = pmFPAfileThisReadout(config->files, view, "PPIMAGE.INPUT");
+
+    psImage *image    = inReadout->image;
+    psImage *mask     = inReadout->mask;
+    // psImage *variance = inReadout->variance;
+
+    // I have the fine image size, I know the binning factor, determine the ruff image size
+    psImageBinning *binning = psImageBinningAlloc();
+    binning->nXfine = image->numCols;
+    binning->nYfine = image->numRows;
+    binning->nXbin  = psMetadataLookupS32 (&status, recipe, "CTE.XBIN");
+    binning->nYbin  = psMetadataLookupS32 (&status, recipe, "CTE.YBIN");
+
+    psImageBinningSetRuffSize(binning, PS_IMAGE_BINNING_CENTER);
+    psImageBinningSetSkip(binning, image);
+
+    pmCell *inCell  = pmFPAfileThisCell (config->files, view, "PPIMAGE.INPUT");
+    pmCell *outCell = pmFPAfileThisCell (config->files, view, "PPIMAGE.CTEMAP");
+    if (!pmCellCopyStructure(outCell, inCell, binning->nXbin, binning->nYbin)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to copy cell structure.");
+        return false;
+    }
+
+    pmReadout *outRO = pmFPAfileThisReadout(config->files, view, "PPIMAGE.CTEMAP");
+    psImage *output = outRO->image;
+
+    // Don't care about the bias: get rid of it, if present
+    psFree(outRO->bias);
+    outRO->bias = psListAlloc(NULL);
+    psMetadataItem *biassec = psMetadataLookup(outCell->concepts, "CELL.BIASSEC");
+    psFree(biassec->data.V);
+    biassec->data.V = psListAlloc(NULL);
+
+    psStats *stats = psStatsAlloc (PS_STAT_SAMPLE_MEDIAN | PS_STAT_SAMPLE_STDEV);
+    psStats *statsDefaults = psStatsAlloc (PS_STAT_SAMPLE_MEDIAN | PS_STAT_SAMPLE_STDEV);
+
+    // measure median and variance for subimages
+    psRegion ruffRegion = {0,0,0,0};
+    psRegion fineRegion = {0,0,0,0};
+    for (int iy = 0; iy < output->numRows; iy++) {
+        for (int ix = 0; ix < output->numCols; ix++) {
+
+            // convert the ruff grid cell to the equivalent fine grid cell
+            ruffRegion = psRegionSet (ix, ix + 1, iy, iy + 1);
+            fineRegion = psImageBinningSetFineRegion (binning, ruffRegion);
+            fineRegion = psRegionForImage (image, fineRegion);
+            if (fineRegion.x0 >= image->numCols || fineRegion.x1 >= image->numCols ||
+                fineRegion.y0 >= image->numRows || fineRegion.y1 >= image->numRows) {
+                continue;
+            }
+
+            psImage *subset  = psImageSubset (image, fineRegion);
+            if (!subset->numCols || !subset->numRows) {
+                psFree (subset);
+                continue;
+            }
+            psImage *submask = NULL;
+            if (mask) {
+                submask = psImageSubset (mask, fineRegion);
+            }
+
+            // reset the default values
+            statsDefaults->tmpData = stats->tmpData; // XXX this is fairly hackish: tmpData is internal storage for stats; the assign drops the reference...
+            *stats = *statsDefaults;
+            statsDefaults->tmpData = NULL;
+
+            psImageStats (stats, subset, submask, 0);
+
+            // XXX need to apply the gain as well
+            output->data.F32[iy][ix] = PS_SQR(stats->sampleStdev) / stats->sampleMedian;
+
+            psFree (subset);
+            psFree (submask);
+        }
+    }
+
+    psFree (binning);
+    psFree (stats);
+    psFree (statsDefaults);
+
+    return true;
+}
Index: /branches/cnb_branches/cnb_branch_20090301/ppMerge/src/Makefile.am
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppMerge/src/Makefile.am	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ppMerge/src/Makefile.am	(revision 23594)
@@ -21,5 +21,5 @@
 	ppMergeFileGroup.c	\
 	ppMergeReadChunk.c	\
-	ppMergeLoop_Threaded.c  \
+	ppMergeLoop.c		\
 	ppMergeSetThreads.c	\
 	ppMergeMask.c		\
Index: /branches/cnb_branches/cnb_branch_20090301/ppMerge/src/ppMerge.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppMerge/src/ppMerge.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ppMerge/src/ppMerge.c	(revision 23594)
@@ -55,4 +55,5 @@
       case PPMERGE_TYPE_FLAT:
       case PPMERGE_TYPE_FRINGE:
+      case PPMERGE_TYPE_CTEMASK:
         if (!ppMergeScaleZero(config)) {
             psErrorStackPrint(stderr, "Error getting scale and zero-points.");
Index: /branches/cnb_branches/cnb_branch_20090301/ppMerge/src/ppMerge.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppMerge/src/ppMerge.h	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ppMerge/src/ppMerge.h	(revision 23594)
@@ -40,4 +40,5 @@
     PPMERGE_TYPE_DARK,                  ///< (Multi-)Dark frame
     PPMERGE_TYPE_MASK,                  ///< Mask frame
+    PPMERGE_TYPE_CTEMASK,		///< CTE Mask based on flat variance
     PPMERGE_TYPE_SHUTTER,               ///< Shutter frame
     PPMERGE_TYPE_FLAT,                  ///< Flat-field frame (dome or sky)
Index: /branches/cnb_branches/cnb_branch_20090301/ppMerge/src/ppMergeArguments.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppMerge/src/ppMergeArguments.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ppMerge/src/ppMergeArguments.c	(revision 23594)
@@ -170,4 +170,7 @@
     psMetadataAddS32(arguments, PS_LIST_TAIL, "-fringe-ysmooth", 0, "Number of smoothing regions in y", 0);
 
+    /** CTEMASK construction parameters */
+    psMetadataAddF32(arguments, PS_LIST_TAIL, "-cte-min", 0, "min allowed value for good CTE", NAN);
+
     /** Shutter construction parameters */
     psMetadataAddS32(arguments, PS_LIST_TAIL, "-shutter-size", 0, "Size for shutter measurement regions", 0);
@@ -268,4 +271,8 @@
       goto VALID;
     }
+    if (strcasecmp(typeStr, "CTEMASK") == 0) {
+      type = PPMERGE_TYPE_CTEMASK;
+      goto VALID;
+    }
     if (strcasecmp(typeStr, "MASK") == 0 ||
         strcasecmp(typeStr, "DARKMASK") == 0 ||
@@ -314,4 +321,7 @@
     VALUE_ARG_RECIPE_INT("-fringe-xsmooth", "FRINGE.XSMOOTH", S32, 0);
     VALUE_ARG_RECIPE_INT("-fringe-ysmooth", "FRINGE.YSMOOTH", S32, 0);
+
+    /** CTEMASK construction parameters */
+    VALUE_ARG_RECIPE_FLOAT("-cte-min",      "CTE.MIN",        F32);
 
     /** Shutter construction parameters */
Index: /branches/cnb_branches/cnb_branch_20090301/ppMerge/src/ppMergeCamera.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppMerge/src/ppMergeCamera.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ppMerge/src/ppMergeCamera.c	(revision 23594)
@@ -326,4 +326,7 @@
         fileType = PM_FPA_FILE_MASK;
         break;
+      case PPMERGE_TYPE_CTEMASK:
+        fileType = PM_FPA_FILE_MASK;
+        break;
       case PPMERGE_TYPE_SHUTTER:
         fileType = PM_FPA_FILE_IMAGE;
Index: /branches/cnb_branches/cnb_branch_20090301/ppMerge/src/ppMergeFiles.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppMerge/src/ppMergeFiles.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ppMerge/src/ppMergeFiles.c	(revision 23594)
@@ -209,4 +209,7 @@
         outSuffix = "MASK";
         break;
+      case PPMERGE_TYPE_CTEMASK:
+        outSuffix = "MASK";
+        break;
       case PPMERGE_TYPE_SHUTTER:
         outSuffix = "SHUTTER";
Index: /branches/cnb_branches/cnb_branch_20090301/ppMerge/src/ppMergeLoop.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppMerge/src/ppMergeLoop.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ppMerge/src/ppMergeLoop.c	(revision 23594)
@@ -1,3 +1,3 @@
-/** @file ppMergeLoop.c
+/** @file ppMergeLoop_Threaded.c
  *
  *  @brief
@@ -6,10 +6,24 @@
  *
  *  @author IfA
- *  @version $Revision: 1.14 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2009-02-01 21:43:05 $
+ *  @version $Revision: 1.9 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2009-02-06 02:44:31 $
  *  Copyright 2009 Institute for Astronomy, University of Hawaii
  */
 
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+
+#include <pslib.h>
+#include <psmodules.h>
+#include <ppStats.h>
+
 #include "ppMerge.h"
+
+// XXX this function is now sufficiently different for the major types, it would make sense to just
+// split it into three: BASIC, SHUTTER, DARK
 
 bool ppMergeLoop(pmConfig *config)
@@ -21,38 +35,39 @@
     int numFiles = psMetadataLookupS32(NULL, arguments, "INPUTS.NUM"); ///< Number of input files
     bool mdok;                          ///< Status of MD lookup
-    bool haveMasks = psMetadataLookupBool(&mdok, arguments, "INPUTS.MASKS"); ///< Do we have masks?
-    bool haveWeights = psMetadataLookupBool(&mdok, arguments, "INPUTS.WEIGHTS"); ///< Do we have weights?
-
-    psArray *inputs = ppMergeFileDataLevel(config, "PPMERGE.INPUT", PM_FPA_LEVEL_READOUT); ///< Input images
-    psArray *masks = NULL, *weights = NULL; ///< Input masks and weights
+    bool haveMasks = psMetadataLookupBool(&mdok, arguments, "INPUTS.MASKS"); // Do we have masks?
+    bool haveVariances = psMetadataLookupBool(&mdok, arguments, "INPUTS.VARIANCES"); // Do we have variances?
+
+    psArray *inputs = ppMergeFileDataLevel(config, "PPMERGE.INPUT", PM_FPA_LEVEL_READOUT); // Input images
+    psArray *masks = NULL, *variances = NULL; // Input masks and variances
     if (haveMasks) {
         masks = ppMergeFileDataLevel(config, "PPMERGE.INPUT.MASK", PM_FPA_LEVEL_READOUT);
     }
-    if (haveWeights) {
-        weights = ppMergeFileDataLevel(config, "PPMERGE.INPUT.WEIGHT", PM_FPA_LEVEL_READOUT);
-    }
-
-    /** General combination parameters */
-    int rows = psMetadataLookupS32(NULL, arguments, "ROWS"); ///< Number of rows to read per chunk
-    int iter = psMetadataLookupS32(NULL, arguments, "ITER"); ///< Number of rejection iterations
-    float rej = psMetadataLookupF32(NULL, arguments, "REJ"); ///< Rejection level
-    float fraclow = psMetadataLookupF32(NULL, arguments, "FRACLOW"); ///< Reject fraction of low pixels
-    float frachigh = psMetadataLookupF32(NULL, arguments, "FRACHIGH"); ///< Reject fraction of hi pixels
-    int nKeep = psMetadataLookupS32(NULL, arguments, "NKEEP"); ///< Minimum number of values to keep
-    psStatsOptions combineStat = psMetadataLookupS32(NULL, arguments, "COMBINE"); ///< Combination statistic
-    bool useWeights = psMetadataLookupBool(NULL, arguments, "WEIGHTS"); ///< Use weights?
-
-    /** Fringe parameters */
-    int fringeNum = psMetadataLookupS32(NULL, arguments, "FRINGE.NUM"); ///* Number of fringe points
-    int fringeSize = psMetadataLookupS32(NULL, arguments, "FRINGE.SIZE"); ///* Size of fringe regions
-    int fringeSmoothX = psMetadataLookupS32(NULL, arguments, "FRINGE.XSMOOTH"); ///< Smoothing regions in x
-    int fringeSmoothY = psMetadataLookupS32(NULL, arguments, "FRINGE.YSMOOTH"); ///< Smoothing regions in y
+    if (haveVariances) {
+        variances = ppMergeFileDataLevel(config, "PPMERGE.INPUT.VARIANCE", PM_FPA_LEVEL_READOUT);
+    }
+
+    int nThreads = psMetadataLookupS32 (&mdok, arguments, "NTHREADS");
+    if (!mdok) nThreads = 0;
+
+    // General combination parameters
+    int iter = psMetadataLookupS32(NULL, arguments, "ITER"); // Number of rejection iterations
+    float rej = psMetadataLookupF32(NULL, arguments, "REJ"); // Rejection level
+    float fraclow = psMetadataLookupF32(NULL, arguments, "FRACLOW"); // Reject fraction of low pixels
+    float frachigh = psMetadataLookupF32(NULL, arguments, "FRACHIGH"); // Reject fraction of hi pixels
+    int nKeep = psMetadataLookupS32(NULL, arguments, "NKEEP"); // Minimum number of values to keep
+    psStatsOptions combineStat = psMetadataLookupS32(NULL, arguments, "COMBINE"); // Combination statistic
+    bool useVariances = psMetadataLookupBool(NULL, arguments, "VARIANCES"); // Use variances?
+
+    // Fringe parameters
+    int fringeNum = psMetadataLookupS32(NULL, arguments, "FRINGE.NUM"); // Number of fringe points
+    int fringeSize = psMetadataLookupS32(NULL, arguments, "FRINGE.SIZE"); // Size of fringe regions
+    int fringeSmoothX = psMetadataLookupS32(NULL, arguments, "FRINGE.XSMOOTH"); // Smoothing regions in x
+    int fringeSmoothY = psMetadataLookupS32(NULL, arguments, "FRINGE.YSMOOTH"); // Smoothing regions in y
 
     // set the mask and mark bit values based on the named masks
-    psImageMaskType maskVal;
-    psImageMaskType markVal;
-    if (!pmConfigMaskSetBits (&maskVal, &markVal, config)) {
-	psError (PS_ERR_UNKNOWN, true, "Unable to define the mask bit values");
-	return false;
+    psImageMaskType maskVal, markVal;
+    if (!pmConfigMaskSetBits(&maskVal, &markVal, config)) {
+        psError (PS_ERR_UNKNOWN, true, "Unable to define the mask bit values");
+        return false;
     }
 
@@ -65,5 +80,5 @@
     combination->iter = iter;
     combination->rej = rej;
-    combination->weights = useWeights;
+    combination->variances = useVariances;
 
     psMetadata *stats = NULL;           ///< Statistics for output
@@ -73,5 +88,5 @@
     }
 
-    pmFPAview *view = pmFPAviewAlloc(0); ///< View to component of interest
+    pmFPAview *view = pmFPAviewAlloc(0); // View to component of interest
 
     // Retrieve data placed on analysis
@@ -103,4 +118,5 @@
       case PPMERGE_TYPE_BIAS:
       case PPMERGE_TYPE_DARK:
+      case PPMERGE_TYPE_CTEMASK:
         break;
       default:
@@ -122,4 +138,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)) {
@@ -143,106 +160,181 @@
             }
 
+            // Update the header
+            {
+                pmHDU *hdu = pmHDUGetHighest(outFPA, outChip, outCell); // HDU for file
+                if (hdu && hdu != lastHDU) {
+                    ppMergeVersionHeader(hdu->header);
+                    lastHDU = hdu;
+                }
+            }
+
+            float shutterRef = NAN;     ///< Reference shutter correction
+            pmReadout *pattern = NULL;
+            if (type == PPMERGE_TYPE_SHUTTER) {
+                shutterRef = pmShutterCorrectionReference(shutters->data[cellNum]);
+                pattern = pmReadoutAlloc(NULL);
+            }
+
             pmReadout *outRO = pmReadoutAlloc(outCell);
 
-            psArray *readouts = psArrayAlloc(numFiles); ///< Input readouts
+            // open the input files (we need to do the work ourselves)
             for (int i = 0; i < numFiles; i++) {
-                // We need to do some of the opening ourselves
                 if (!ppMergeFileOpenInput(config, view, i)) {
                     psError(PS_ERR_UNKNOWN, false, "Unable to open file %d", i);
                     goto ERROR;
                 }
-
+            }
+
+            ppMergeFileGroup *fileGroup = NULL;
+            psArray *fileGroups = psArrayAlloc(nThreads + 1);
+
+            // Generate readouts for each input file in each file group
+            for (int i = 0; i < fileGroups->n; i++) {
+                psArray *readouts = psArrayAlloc(numFiles); ///< Input readouts
+                for (int j = 0; j < numFiles; j++) {
+                    pmFPAfile *input = pmFPAfileSelectSingle(config->files, "PPMERGE.INPUT", j);
+                    pmCell *inCell = pmFPAviewThisCell(view, input->fpa); ///< Input cell
+                    readouts->data[j] = pmReadoutAlloc(inCell);
+                }
+
+                fileGroup = ppMergeFileGroupAlloc();
+                fileGroup->readouts = readouts;
+                fileGroup->read = false;
+                fileGroup->busy = false;
+                fileGroup->lastScan = 0;
+                fileGroup->firstScan = 0;
+                fileGroups->data[i] = fileGroup;
+            }
+
+            // call the init functions
+            switch (type) {
+              case PPMERGE_TYPE_CTEMASK:
+              case PPMERGE_TYPE_BIAS:
+              case PPMERGE_TYPE_FLAT:
+              case PPMERGE_TYPE_FRINGE:
+                psAssert (fileGroups->n > 0, "no valid file groups defined");
+                fileGroup = fileGroups->data[0];
+                if (!pmReadoutCombinePrepare(outRO, fileGroup->readouts, combination)) {
+                    goto ERROR;
+                }
+                break;
+              case PPMERGE_TYPE_DARK:
+                psAssert (fileGroups->n > 0, "no valid file groups defined");
+                fileGroup = fileGroups->data[0];
+                if (!pmDarkCombinePrepare(outCell, fileGroup->readouts, darkOrdinates, darkNorm)) {
+                    goto ERROR;
+                }
+                break;
+              case PPMERGE_TYPE_SHUTTER:
+                psAssert (fileGroups->n > 0, "no valid file groups defined");
+                fileGroup = fileGroups->data[0];
+                if (!pmShutterCorrectionGeneratePrepare(outRO, pattern, fileGroup->readouts, maskVal)) {
+                    goto ERROR;
+                }
+                break;
+              default:
+                psAbort("Should never get here.");
+            }
+
+            // Read input data by chunks
+            psTimerStart("ppMergeLoop");
+            for (int numChunk = 0; true; numChunk++) {
+
+                bool status = false;
+                fileGroup = ppMergeReadChunk(&status, fileGroups, config, numChunk);
+                if (!status) {
+                    // Something went wrong
+                    goto ERROR;
+                }
+                if (!fileGroup) {
+                    // Nothing more to read
+                    break;
+                }
+
+                // Start a job
+                switch (type) {
+                  case PPMERGE_TYPE_CTEMASK:
+                  case PPMERGE_TYPE_BIAS:
+                  case PPMERGE_TYPE_FLAT:
+                  case PPMERGE_TYPE_FRINGE: {
+                      psThreadJob *job = psThreadJobAlloc("PPMERGE_READOUT_COMBINE"); ///< Job to start
+
+                      // Construct the arguments for this job
+                      psArrayAdd(job->args, 1, outRO);
+                      psArrayAdd(job->args, 1, fileGroup);
+                      psArrayAdd(job->args, 1, zeros);
+                      psArrayAdd(job->args, 1, scales);
+                      psArrayAdd(job->args, 1, combination);
+
+                      // call: pmReadoutCombine(outRO, fileGroup->readouts, zeros, scales, combination);
+                      if (!psThreadJobAddPending(job)) {
+                          goto ERROR;
+                      }
+                      break;
+                  }
+                  case PPMERGE_TYPE_DARK: {
+                      psThreadJob *job = psThreadJobAlloc ("PPMERGE_DARK_COMBINE"); ///< Job to start
+
+                      // construct the arguments for this job
+                      psArrayAdd(job->args, 1, outCell);
+                      psArrayAdd(job->args, 1, fileGroup);
+                      psArrayAdd(job->args, 1, psScalarAlloc(iter, PS_TYPE_S32));
+                      psArrayAdd(job->args, 1, psScalarAlloc(rej, PS_TYPE_F32));
+                      psArrayAdd(job->args, 1, psScalarAlloc(maskVal, PS_TYPE_IMAGE_MASK));
+
+                      // call: pmDarkCombine(outCell, fileGroup->readouts, iter, rej, maskVal);
+                      if (!psThreadJobAddPending(job)) {
+                          goto ERROR;
+                      }
+                      break;
+                  }
+                  case PPMERGE_TYPE_SHUTTER: {
+                      psThreadJob *job = psThreadJobAlloc ("PPMERGE_SHUTTER_CORRECTION");
+
+                      // construct the arguments for this job
+                      psArrayAdd(job->args, 1, outRO);
+                      psArrayAdd(job->args, 1, pattern);
+                      psArrayAdd(job->args, 1, fileGroup);
+                      psArrayAdd(job->args, 1, psScalarAlloc(shutterRef, PS_TYPE_F32));
+                      psArrayAdd(job->args, 1, shutters->data[cellNum]);
+                      psArrayAdd(job->args, 1, psScalarAlloc(iter, PS_TYPE_S32));
+                      psArrayAdd(job->args, 1, psScalarAlloc(rej, PS_TYPE_F32));
+                      psArrayAdd(job->args, 1, psScalarAlloc(maskVal, PS_TYPE_IMAGE_MASK));
+
+                      // call: pmShutterCorrectionGenerate(outRO, pattern, fileGroup->readouts, shutterRef,
+                      //                                   shutters->data[cellNum], iter, rej, maskVal);
+                      if (!psThreadJobAddPending (job)) {
+                          goto ERROR;
+                      }
+                      break;
+                  }
+                  default:
+                    psAbort("Should never get here.");
+                }
+            }
+
+            // Wait for the threads to finish and manage results
+            if (!psThreadPoolWait(false)) {
+                psError(PS_ERR_UNKNOWN, false, "Unable to combine images.");
+                return false;
+            }
+
+            // we don't care about the results, just dump the done queue jobs
+            psThreadJob *job = NULL;    // Job to dump
+            while ((job = psThreadJobGetDone())) {
+                psFree (job);
+            }
+
+            psFree(fileGroups);
+
+            // XXX eventually need to keep both the shutter and the pattern, as we do with dark
+            psFree(pattern);
+
+            // Get list of cells for concepts averaging
+            psList *inCells = psListAlloc(NULL); // List of cells
+            for (int i = 0; i < numFiles; i++) {
                 pmFPAfile *input = pmFPAfileSelectSingle(config->files, "PPMERGE.INPUT", i);
                 pmCell *inCell = pmFPAviewThisCell(view, input->fpa); ///< Input cell
-                readouts->data[i] = pmReadoutAlloc(inCell);
-            }
-
-            float shutterRef = NAN;     ///< Reference shutter correction
-            if (type == PPMERGE_TYPE_SHUTTER) {
-                shutterRef = pmShutterCorrectionReference(shutters->data[cellNum]);
-            }
-
-            // Read convolutions by chunks
-            bool more = true;               // More to read?
-            for (int numChunk = 0; more; numChunk++) {
-                psTrace("ppStack", 2, "Initial stack of chunk %d....\n", numChunk);
-                for (int i = 0; i < numFiles; i++) {
-                    pmReadout *inRO = readouts->data[i]; ///< Input readout
-
-                    // Read a chunk from a file
-                    #define READ_CHUNK(NAME,TYPE) { \
-                        pmFPAfile *file = pmFPAfileSelectSingle(config->files, NAME, i); \
-                        if (!pmReadoutReadChunk##TYPE(inRO, file->fits, 0, rows, 0, config)) { \
-                            psError(PS_ERR_IO, false, "Unable to read chunk %d for file %s %d", \
-                                    numChunk, NAME, i); \
-                            psFree(readouts); \
-                            psFree(outRO); \
-                            goto ERROR; \
-                        } \
-                    }
-
-                    READ_CHUNK("PPMERGE.INPUT", /* Blank */);
-                    if (haveMasks) {
-                        READ_CHUNK("PPMERGE.INPUT.MASK", Mask);
-                    }
-                    if (haveWeights) {
-                        READ_CHUNK("PPMERGE.INPUT.WEIGHT", Weight);
-                    }
-                }
-
-                switch (type) {
-                  case PPMERGE_TYPE_SHUTTER:
-                    if (!pmShutterCorrectionGenerate(outRO, NULL, readouts, shutterRef,
-                                                     shutters->data[cellNum], iter, rej, maskVal)) {
-                        psFree(readouts);
-                        psFree(outRO);
-                        goto ERROR;
-                    }
-                    break;
-                  case PPMERGE_TYPE_DARK:
-                    if (!pmDarkCombine(outCell, readouts, darkOrdinates, darkNorm, iter, rej, maskVal)) {
-                        psFree(readouts);
-                        psFree(outRO);
-                        goto ERROR;
-                    }
-                    break;
-                  case PPMERGE_TYPE_BIAS:
-                  case PPMERGE_TYPE_FLAT:
-                  case PPMERGE_TYPE_FRINGE:
-                    if (!pmReadoutCombine(outRO, readouts, zeros, scales, combination)) {
-                        psFree(readouts);
-                        psFree(outRO);
-                        goto ERROR;
-                    }
-                    break;
-                  default:
-                    psAbort("Should never get here.");
-                }
-
-
-                for (int i = 0; i < numFiles && more; i++) {
-                    pmReadout *inRO = readouts->data[i];
-
-                    // Check to see if there's more chunks to read
-                    #define MORE_CHUNK(NAME,TYPE) { \
-                        pmFPAfile *file = pmFPAfileSelectSingle(config->files, NAME, i); \
-                        more &= pmReadoutMore##TYPE(inRO, file->fits, 0, rows, config); \
-                    }
-
-                    MORE_CHUNK("PPMERGE.INPUT", /* Blank */);
-                    if (haveMasks) {
-                        MORE_CHUNK("PPMERGE.INPUT.MASK", Mask);
-                    }
-                    if (haveWeights) {
-                        MORE_CHUNK("PPMERGE.INPUT.WEIGHT", Weight);
-                    }
-                }
-            }
-
-            // Get list of cells for concepts averaging
-            psList *inCells = psListAlloc(NULL); ///< List of cells
-            for (int i = 0; i < numFiles; i++) {
-                pmReadout *readout = readouts->data[i]; ///< Readout of interest
-                psListAdd(inCells, PS_LIST_TAIL, readout->parent);
+                psListAdd(inCells, PS_LIST_TAIL, inCell);
             }
             if (!pmConceptsAverageCells(outCell, inCells, NULL, NULL, true)) {
@@ -253,6 +345,4 @@
             }
             psFree(inCells);
-
-            psFree(readouts);
 
             // Plug supplementary images into their own FPAs
@@ -266,11 +356,11 @@
                 psFree(countsRO);
 
-		// XXX EAM 2009.01.18 : sigmaCell and countsCell need to have their concepts copied from outCell.
-		// This was causing segfaults for VYSOS5; Why did this ever work for SIMTEST?
-		if (!pmConceptsCopyCell(countsCell, outCell)) {
-		    psError(PS_ERR_UNKNOWN, false, "Unable to copy cell concepts.");
-		    psFree(outRO);
-		    goto ERROR;
-		}
+                // XXX EAM 2009.01.18 : sigmaCell and countsCell need to have their concepts copied from outCell.
+                // This was causing segfaults for VYSOS5; Why did this ever work for SIMTEST?
+                if (!pmConceptsCopyCell(countsCell, outCell)) {
+                    psError(PS_ERR_UNKNOWN, false, "Unable to copy cell concepts.");
+                    psFree(outRO);
+                    goto ERROR;
+                }
 
                 pmCell *sigmaCell = pmFPAfileThisCell(config->files, view, "PPMERGE.OUTPUT.SIGMA");
@@ -282,9 +372,9 @@
                 psFree(sigmaRO);
 
-		if (!pmConceptsCopyCell(sigmaCell, outCell)) {
-		    psError(PS_ERR_UNKNOWN, false, "Unable to copy cell concepts.");
-		    psFree(outRO);
-		    goto ERROR;
-		}
+                if (!pmConceptsCopyCell(sigmaCell, outCell)) {
+                    psError(PS_ERR_UNKNOWN, false, "Unable to copy cell concepts.");
+                    psFree(outRO);
+                    goto ERROR;
+                }
             }
 
@@ -316,7 +406,31 @@
             }
 
-            if (!ppStatsFPA(stats, outFPA, view, maskVal, config)) {
+            if (stats && !ppStatsFPA(stats, outFPA, view, maskVal, config)) {
                 psError(PS_ERR_UNKNOWN, true, "Unable to generate stats for image.");
                 goto ERROR;
+            }
+
+	    // calculate CTEMASK after stats so stats reflect median image
+            if (type == PPMERGE_TYPE_CTEMASK && outRO) {
+		// need to apply range cuts on the output image
+		psAssert (outRO->mask, "mask is not defined");
+		psAssert (outRO->mask->numCols == outRO->image->numCols, "mismatch between image and mask");
+		psAssert (outRO->mask->numRows == outRO->image->numRows, "mismatch between image and mask");
+
+		// CTEMASK parameters
+		float cteMin = psMetadataLookupF32(NULL, arguments, "CTE.MIN"); // Number of fringe points
+
+		char *cteMaskName = psMetadataLookupStr (&mdok, config->arguments, "MASK.SET.VALUE");
+		psImageMaskType cteMaskValue = pmConfigMaskGet(cteMaskName, config);
+
+		psF32 **outputImage = outRO->image->data.F32; 
+		psImageMaskType **outputMask = outRO->mask->data.PS_TYPE_IMAGE_MASK_DATA; 
+		for (int iy = 0; iy < outRO->image->numRows; iy++) {
+		    for (int ix = 0; ix < outRO->image->numCols; ix++) {
+			if (outputImage[iy][ix] < cteMin) {
+			    outputMask[iy][ix] |= cteMaskValue;
+			}
+		    }
+		}
             }
 
@@ -369,5 +483,5 @@
     psFree(inputs);
     psFree(masks);
-    psFree(weights);
+    psFree(variances);
     psFree(stats);
     return true;
@@ -378,5 +492,5 @@
     psFree(inputs);
     psFree(masks);
-    psFree(weights);
+    psFree(variances);
     psFree(stats);
     return false;
Index: anches/cnb_branches/cnb_branch_20090301/ppMerge/src/ppMergeLoop_Threaded.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppMerge/src/ppMergeLoop_Threaded.c	(revision 23593)
+++ 	(revision )
@@ -1,471 +1,0 @@
-/** @file ppMergeLoop_Threaded.c
- *
- *  @brief
- *
- *  @ingroup ppMerge
- *
- *  @author IfA
- *  @version $Revision: 1.9 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2009-02-06 02:44:31 $
- *  Copyright 2009 Institute for Astronomy, University of Hawaii
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <string.h>
-
-#include <pslib.h>
-#include <psmodules.h>
-#include <ppStats.h>
-
-#include "ppMerge.h"
-
-// XXX this function is now sufficiently different for the major types, it would make sense to just
-// split it into three: BASIC, SHUTTER, DARK
-
-bool ppMergeLoop(pmConfig *config)
-{
-    assert(config);
-
-    psMetadata *arguments = config->arguments; ///< Arguments
-    ppMergeType type = psMetadataLookupS32(NULL, config->arguments, "TYPE"); ///< Type of frame
-    int numFiles = psMetadataLookupS32(NULL, arguments, "INPUTS.NUM"); ///< Number of input files
-    bool mdok;                          ///< Status of MD lookup
-    bool haveMasks = psMetadataLookupBool(&mdok, arguments, "INPUTS.MASKS"); // Do we have masks?
-    bool haveVariances = psMetadataLookupBool(&mdok, arguments, "INPUTS.VARIANCES"); // Do we have variances?
-
-    psArray *inputs = ppMergeFileDataLevel(config, "PPMERGE.INPUT", PM_FPA_LEVEL_READOUT); // Input images
-    psArray *masks = NULL, *variances = NULL; // Input masks and variances
-    if (haveMasks) {
-        masks = ppMergeFileDataLevel(config, "PPMERGE.INPUT.MASK", PM_FPA_LEVEL_READOUT);
-    }
-    if (haveVariances) {
-        variances = ppMergeFileDataLevel(config, "PPMERGE.INPUT.VARIANCE", PM_FPA_LEVEL_READOUT);
-    }
-
-    int nThreads = psMetadataLookupS32 (&mdok, arguments, "NTHREADS");
-    if (!mdok) nThreads = 0;
-
-    // General combination parameters
-    int iter = psMetadataLookupS32(NULL, arguments, "ITER"); // Number of rejection iterations
-    float rej = psMetadataLookupF32(NULL, arguments, "REJ"); // Rejection level
-    float fraclow = psMetadataLookupF32(NULL, arguments, "FRACLOW"); // Reject fraction of low pixels
-    float frachigh = psMetadataLookupF32(NULL, arguments, "FRACHIGH"); // Reject fraction of hi pixels
-    int nKeep = psMetadataLookupS32(NULL, arguments, "NKEEP"); // Minimum number of values to keep
-    psStatsOptions combineStat = psMetadataLookupS32(NULL, arguments, "COMBINE"); // Combination statistic
-    bool useVariances = psMetadataLookupBool(NULL, arguments, "VARIANCES"); // Use variances?
-
-    // Fringe parameters
-    int fringeNum = psMetadataLookupS32(NULL, arguments, "FRINGE.NUM"); // Number of fringe points
-    int fringeSize = psMetadataLookupS32(NULL, arguments, "FRINGE.SIZE"); // Size of fringe regions
-    int fringeSmoothX = psMetadataLookupS32(NULL, arguments, "FRINGE.XSMOOTH"); // Smoothing regions in x
-    int fringeSmoothY = psMetadataLookupS32(NULL, arguments, "FRINGE.YSMOOTH"); // Smoothing regions in y
-
-    // set the mask and mark bit values based on the named masks
-    psImageMaskType maskVal, markVal;
-    if (!pmConfigMaskSetBits(&maskVal, &markVal, config)) {
-        psError (PS_ERR_UNKNOWN, true, "Unable to define the mask bit values");
-        return false;
-    }
-
-    pmCombineParams *combination = pmCombineParamsAlloc(combineStat); ///< Combination parameters
-    combination->maskVal = maskVal;
-    combination->blank = pmConfigMaskGet("BLANK", config);
-    combination->nKeep = nKeep;
-    combination->fracHigh = frachigh;
-    combination->fracLow = fraclow;
-    combination->iter = iter;
-    combination->rej = rej;
-    combination->variances = useVariances;
-
-    psMetadata *stats = NULL;           ///< Statistics for output
-    if (psMetadataLookup(config->arguments, "STATS.NAME")) {
-        stats = psMetadataAlloc();
-        psMetadataAddMetadata(config->arguments, PS_LIST_TAIL, "STATS.DATA", 0, "Statistics output", stats);
-    }
-
-    pmFPAview *view = pmFPAviewAlloc(0); // View to component of interest
-
-    // Retrieve data placed on analysis
-    psVector *scales = NULL, *zeros = NULL; ///< Scale and zeroes for combination
-    psArray *shutters = NULL;           ///< Shutter correction data
-    switch (type) {
-      case PPMERGE_TYPE_FRINGE:
-        zeros = psMetadataLookupPtr(NULL, arguments, "ZEROS");
-        if (!zeros) {
-            psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to find ZEROS");
-            goto ERROR;
-        }
-        // Flow through
-      case PPMERGE_TYPE_FLAT:
-        scales = psMetadataLookupPtr(NULL, arguments, "SCALES");
-        if (!scales) {
-            psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to find SCALES");
-            goto ERROR;
-        }
-        break;
-      case PPMERGE_TYPE_SHUTTER:
-        shutters = psMetadataLookupPtr(NULL, arguments, "SHUTTER");
-        if (!shutters) {
-            psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to find SHUTTER");
-            goto ERROR;
-        }
-        break;
-      case PPMERGE_TYPE_MASK:
-      case PPMERGE_TYPE_BIAS:
-      case PPMERGE_TYPE_DARK:
-        break;
-      default:
-        psAbort("Should never get here.");
-    }
-
-    // Dark parameters
-    psArray *darkOrdinates = psMetadataLookupPtr(NULL, arguments, "DARK.ORDINATES"); ///< Dark info
-    psString darkNorm = psMetadataLookupStr(&mdok, arguments, "DARK.NORM"); ///< Dark normalisation
-
-
-    if (!ppMergeFileActivate(config, PPMERGE_FILES_ALL, true)) {
-        psError(PS_ERR_UNKNOWN, false, "Unable to activate files.");
-        goto ERROR;
-    }
-    psString outName = ppMergeOutputFile(config); ///< Name of output file
-    pmFPAfile *output = psMetadataLookupPtr(NULL, config->files, outName); ///< Output file
-    psFree(outName);
-    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)) {
-        goto ERROR;
-    }
-    pmChip *outChip;                    ///< Chip of interest
-    while ((outChip = pmFPAviewNextChip(view, outFPA, 1))) {
-        if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
-            goto ERROR;
-        }
-        pmCell *outCell;                ///< Cell of interest
-        while ((outCell = pmFPAviewNextCell(view, outFPA, 1))) {
-            if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
-                goto ERROR;
-            }
-
-            pmHDU *hdu = pmHDUGetLowest(outFPA, outChip, outCell); ///< HDU for cell
-            if (!hdu || hdu->blankPHU) {
-                // 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;
-                }
-            }
-
-            float shutterRef = NAN;     ///< Reference shutter correction
-            pmReadout *pattern = NULL;
-            if (type == PPMERGE_TYPE_SHUTTER) {
-                shutterRef = pmShutterCorrectionReference(shutters->data[cellNum]);
-                pattern = pmReadoutAlloc(NULL);
-            }
-
-            pmReadout *outRO = pmReadoutAlloc(outCell);
-
-            // open the input files (we need to do the work ourselves)
-            for (int i = 0; i < numFiles; i++) {
-                if (!ppMergeFileOpenInput(config, view, i)) {
-                    psError(PS_ERR_UNKNOWN, false, "Unable to open file %d", i);
-                    goto ERROR;
-                }
-            }
-
-            ppMergeFileGroup *fileGroup = NULL;
-            psArray *fileGroups = psArrayAlloc(nThreads + 1);
-
-            // Generate readouts for each input file in each file group
-            for (int i = 0; i < fileGroups->n; i++) {
-                psArray *readouts = psArrayAlloc(numFiles); ///< Input readouts
-                for (int j = 0; j < numFiles; j++) {
-                    pmFPAfile *input = pmFPAfileSelectSingle(config->files, "PPMERGE.INPUT", j);
-                    pmCell *inCell = pmFPAviewThisCell(view, input->fpa); ///< Input cell
-                    readouts->data[j] = pmReadoutAlloc(inCell);
-                }
-
-                fileGroup = ppMergeFileGroupAlloc();
-                fileGroup->readouts = readouts;
-                fileGroup->read = false;
-                fileGroup->busy = false;
-                fileGroup->lastScan = 0;
-                fileGroup->firstScan = 0;
-                fileGroups->data[i] = fileGroup;
-            }
-
-            // call the init functions
-            switch (type) {
-              case PPMERGE_TYPE_BIAS:
-              case PPMERGE_TYPE_FLAT:
-              case PPMERGE_TYPE_FRINGE:
-                psAssert (fileGroups->n > 0, "no valid file groups defined");
-                fileGroup = fileGroups->data[0];
-                if (!pmReadoutCombinePrepare(outRO, fileGroup->readouts, combination)) {
-                    goto ERROR;
-                }
-                break;
-              case PPMERGE_TYPE_DARK:
-                psAssert (fileGroups->n > 0, "no valid file groups defined");
-                fileGroup = fileGroups->data[0];
-                if (!pmDarkCombinePrepare(outCell, fileGroup->readouts, darkOrdinates, darkNorm)) {
-                    goto ERROR;
-                }
-                break;
-              case PPMERGE_TYPE_SHUTTER:
-                psAssert (fileGroups->n > 0, "no valid file groups defined");
-                fileGroup = fileGroups->data[0];
-                if (!pmShutterCorrectionGeneratePrepare(outRO, pattern, fileGroup->readouts, maskVal)) {
-                    goto ERROR;
-                }
-                break;
-              default:
-                psAbort("Should never get here.");
-            }
-
-            // Read input data by chunks
-            psTimerStart("ppMergeLoop");
-            for (int numChunk = 0; true; numChunk++) {
-
-                bool status = false;
-                fileGroup = ppMergeReadChunk(&status, fileGroups, config, numChunk);
-                if (!status) {
-                    // Something went wrong
-                    goto ERROR;
-                }
-                if (!fileGroup) {
-                    // Nothing more to read
-                    break;
-                }
-
-                // Start a job
-                switch (type) {
-                  case PPMERGE_TYPE_BIAS:
-                  case PPMERGE_TYPE_FLAT:
-                  case PPMERGE_TYPE_FRINGE: {
-                      psThreadJob *job = psThreadJobAlloc("PPMERGE_READOUT_COMBINE"); ///< Job to start
-
-                      // Construct the arguments for this job
-                      psArrayAdd(job->args, 1, outRO);
-                      psArrayAdd(job->args, 1, fileGroup);
-                      psArrayAdd(job->args, 1, zeros);
-                      psArrayAdd(job->args, 1, scales);
-                      psArrayAdd(job->args, 1, combination);
-
-                      // call: pmReadoutCombine(outRO, fileGroup->readouts, zeros, scales, combination);
-                      if (!psThreadJobAddPending(job)) {
-                          goto ERROR;
-                      }
-                      break;
-                  }
-                  case PPMERGE_TYPE_DARK: {
-                      psThreadJob *job = psThreadJobAlloc ("PPMERGE_DARK_COMBINE"); ///< Job to start
-
-                      // construct the arguments for this job
-                      psArrayAdd(job->args, 1, outCell);
-                      psArrayAdd(job->args, 1, fileGroup);
-                      psArrayAdd(job->args, 1, psScalarAlloc(iter, PS_TYPE_S32));
-                      psArrayAdd(job->args, 1, psScalarAlloc(rej, PS_TYPE_F32));
-                      psArrayAdd(job->args, 1, psScalarAlloc(maskVal, PS_TYPE_IMAGE_MASK));
-
-                      // call: pmDarkCombine(outCell, fileGroup->readouts, iter, rej, maskVal);
-                      if (!psThreadJobAddPending(job)) {
-                          goto ERROR;
-                      }
-                      break;
-                  }
-                  case PPMERGE_TYPE_SHUTTER: {
-                      psThreadJob *job = psThreadJobAlloc ("PPMERGE_SHUTTER_CORRECTION");
-
-                      // construct the arguments for this job
-                      psArrayAdd(job->args, 1, outRO);
-                      psArrayAdd(job->args, 1, pattern);
-                      psArrayAdd(job->args, 1, fileGroup);
-                      psArrayAdd(job->args, 1, psScalarAlloc(shutterRef, PS_TYPE_F32));
-                      psArrayAdd(job->args, 1, shutters->data[cellNum]);
-                      psArrayAdd(job->args, 1, psScalarAlloc(iter, PS_TYPE_S32));
-                      psArrayAdd(job->args, 1, psScalarAlloc(rej, PS_TYPE_F32));
-                      psArrayAdd(job->args, 1, psScalarAlloc(maskVal, PS_TYPE_IMAGE_MASK));
-
-                      // call: pmShutterCorrectionGenerate(outRO, pattern, fileGroup->readouts, shutterRef,
-                      //                                   shutters->data[cellNum], iter, rej, maskVal);
-                      if (!psThreadJobAddPending (job)) {
-                          goto ERROR;
-                      }
-                      break;
-                  }
-                  default:
-                    psAbort("Should never get here.");
-                }
-            }
-
-            // Wait for the threads to finish and manage results
-            if (!psThreadPoolWait(false)) {
-                psError(PS_ERR_UNKNOWN, false, "Unable to combine images.");
-                return false;
-            }
-
-            // we don't care about the results, just dump the done queue jobs
-            psThreadJob *job = NULL;    // Job to dump
-            while ((job = psThreadJobGetDone())) {
-                psFree (job);
-            }
-
-            psFree(fileGroups);
-
-            // XXX eventually need to keep both the shutter and the pattern, as we do with dark
-            psFree(pattern);
-
-            // Get list of cells for concepts averaging
-            psList *inCells = psListAlloc(NULL); // List of cells
-            for (int i = 0; i < numFiles; i++) {
-                pmFPAfile *input = pmFPAfileSelectSingle(config->files, "PPMERGE.INPUT", i);
-                pmCell *inCell = pmFPAviewThisCell(view, input->fpa); ///< Input cell
-                psListAdd(inCells, PS_LIST_TAIL, inCell);
-            }
-            if (!pmConceptsAverageCells(outCell, inCells, NULL, NULL, true)) {
-                psError(PS_ERR_UNKNOWN, false, "Unable to average cell concepts.");
-                psFree(inCells);
-                psFree(outRO);
-                goto ERROR;
-            }
-            psFree(inCells);
-
-            // Plug supplementary images into their own FPAs
-            {
-                pmCell *countsCell = pmFPAfileThisCell(config->files, view, "PPMERGE.OUTPUT.COUNT");
-                pmReadout *countsRO = pmReadoutAlloc(countsCell); ///< Readout with count of inputs per pixel
-                psImage *counts = psMetadataLookupPtr(NULL, outRO->analysis, PM_READOUT_STACK_ANALYSIS_COUNT);
-                countsRO->image = psImageCopy(countsRO->image, counts, PS_TYPE_F32);
-                psMetadataRemoveKey(outRO->analysis, PM_READOUT_STACK_ANALYSIS_COUNT);
-                countsRO->data_exists = countsCell->data_exists = countsCell->parent->data_exists = true;
-                psFree(countsRO);
-
-                // XXX EAM 2009.01.18 : sigmaCell and countsCell need to have their concepts copied from outCell.
-                // This was causing segfaults for VYSOS5; Why did this ever work for SIMTEST?
-                if (!pmConceptsCopyCell(countsCell, outCell)) {
-                    psError(PS_ERR_UNKNOWN, false, "Unable to copy cell concepts.");
-                    psFree(outRO);
-                    goto ERROR;
-                }
-
-                pmCell *sigmaCell = pmFPAfileThisCell(config->files, view, "PPMERGE.OUTPUT.SIGMA");
-                pmReadout *sigmaRO = pmReadoutAlloc(sigmaCell); ///< Readout with stdev per pixel
-                psImage *sigma = psMetadataLookupPtr(NULL, outRO->analysis, PM_READOUT_STACK_ANALYSIS_SIGMA);
-                sigmaRO->image = psImageCopy(sigmaRO->image, sigma, PS_TYPE_F32);
-                psMetadataRemoveKey(outRO->analysis, PM_READOUT_STACK_ANALYSIS_SIGMA);
-                sigmaRO->data_exists = sigmaCell->data_exists = sigmaCell->parent->data_exists = true;
-                psFree(sigmaRO);
-
-                if (!pmConceptsCopyCell(sigmaCell, outCell)) {
-                    psError(PS_ERR_UNKNOWN, false, "Unable to copy cell concepts.");
-                    psFree(outRO);
-                    goto ERROR;
-                }
-            }
-
-            // Measure the fringes for this cell
-            //
-            // XXX Need to deal with multiple components: we will do this by building up the components
-            // Read the existing fringe measurements
-            // Use existing regions to measure fringe statistics
-            // Add the new fringe measurements to the existing fringe measurements.
-            // Write the appended fringe measurements.
-            // Read in the "output" file to get the existing components.
-            // Put the new readout into the cell after the existing readouts.
-            if (type == PPMERGE_TYPE_FRINGE && outRO) {
-                pmFringeRegions *regions = pmFringeRegionsAlloc(fringeNum, fringeSize, fringeSize,
-                                                                fringeSmoothX, fringeSmoothY);
-                pmFringeStats *fringe = pmFringeStatsMeasure(regions, outRO, maskVal);
-                psFree(regions);
-                if (!fringe) {
-                    psError(PS_ERR_UNKNOWN, false, "Unable to measure fringe statistics.\n");
-                    psFree(outRO);
-                    goto ERROR;
-                }
-
-                psArray *fringes = psArrayAlloc(1); ///< Array of fringes
-                fringes->data[0] = fringe;
-
-                pmFringesFormat(outCell, NULL, fringes);
-                psFree(fringes);        // Drop reference
-            }
-
-            if (stats && !ppStatsFPA(stats, outFPA, view, maskVal, config)) {
-                psError(PS_ERR_UNKNOWN, true, "Unable to generate stats for image.");
-                goto ERROR;
-            }
-
-            psFree(outRO);
-            cellNum++;
-
-            if (!pmFPAfileIOChecks(config, view, PM_FPA_AFTER)) {
-                goto ERROR;
-            }
-        }
-
-        if (outChip->data_exists) {
-            psList *inChips = psListAlloc(NULL);
-            for (int i=0; i < numFiles; i++) {
-                pmChip *chip = pmFPAviewThisChip(view, ((pmFPAfile *)inputs->data[i])->fpa);
-                psListAdd(inChips, PS_LIST_TAIL, chip);
-            }
-            if (!pmConceptsAverageChips(outChip, inChips, true)) {
-                psError(PS_ERR_UNKNOWN, false, "Unable to average Chip concepts.");
-                psFree(inChips);
-                goto ERROR;
-            }
-            psFree(inChips);
-        }
-        if (!pmFPAfileIOChecks(config, view, PM_FPA_AFTER)) {
-            goto ERROR;
-        }
-    }
-
-    // Get list of FPAs for concepts averaging
-    psList *inFPAs = psListAlloc(NULL); ///< List of FPAs
-    for (int i = 0; i < numFiles; i++) {
-        pmFPAfile *input = inputs->data[i]; ///< Input file
-        psListAdd(inFPAs, PS_LIST_TAIL, input->fpa);
-    }
-    if (!pmConceptsAverageFPAs(output->fpa, inFPAs)) {
-        psError(PS_ERR_UNKNOWN, false, "Unable to average FPA concepts.");
-        psFree(inFPAs);
-        goto ERROR;
-    }
-    psFree(inFPAs);
-
-
-    if (!pmFPAfileIOChecks(config, view, PM_FPA_AFTER)) {
-        goto ERROR;
-    }
-
-    psFree(view);
-    psFree(combination);
-    psFree(inputs);
-    psFree(masks);
-    psFree(variances);
-    psFree(stats);
-    return true;
-
-ERROR:
-    psFree(view);
-    psFree(combination);
-    psFree(inputs);
-    psFree(masks);
-    psFree(variances);
-    psFree(stats);
-    return false;
-}
-
Index: /branches/cnb_branches/cnb_branch_20090301/ppMerge/src/ppMergeScaleZero.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppMerge/src/ppMergeScaleZero.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ppMerge/src/ppMergeScaleZero.c	(revision 23594)
@@ -35,4 +35,5 @@
       case PPMERGE_TYPE_BIAS:
       case PPMERGE_TYPE_DARK:
+      case PPMERGE_TYPE_CTEMASK:
         // Nothing to measure
         return true;
Index: /branches/cnb_branches/cnb_branch_20090301/ppSim/src/Makefile.am
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppSim/src/Makefile.am	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ppSim/src/Makefile.am	(revision 23594)
@@ -45,4 +45,5 @@
 	ppSimRandomGaussian.c	  \
 	ppSimBadPixels.c          \
+	ppSimBadCTE.c             \
 	ppSimVersion.c
 
Index: /branches/cnb_branches/cnb_branch_20090301/ppSim/src/ppSim.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppSim/src/ppSim.h	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ppSim/src/ppSim.h	(revision 23594)
@@ -67,6 +67,6 @@
 } ppSimGalaxy;
 
-ppSimStar *ppSimStarAlloc ();
-ppSimGalaxy *ppSimGalaxyAlloc ();
+ppSimStar *ppSimStarAlloc(void);
+ppSimGalaxy *ppSimGalaxyAlloc(void);
 
 /// Parse command-line arguments
@@ -135,4 +135,9 @@
 
 
+// add a bad CTE region
+bool ppSimBadCTE(psImage *image,        // Signal image, modified and returned
+                 const pmConfig *config // configuration
+  );
+
 /// Add bad pixels to an image
 bool ppSimBadPixels(pmReadout *readout, ///< Readout for which to generate bad pixels
@@ -184,5 +189,5 @@
 double ppSimRandomGaussian (const psRandom *rnd, double mean, double sigma);
 double ppSimRandomGaussianNorm (const psRandom *rnd);
-void ppSimRandomGaussianFree();
+void ppSimRandomGaussianFree(void);
 
 bool ppSimPhotomFiles (pmConfig *config, pmFPAfile *fakeFile, pmFPAfile *forceFile);
Index: /branches/cnb_branches/cnb_branch_20090301/ppSim/src/ppSimBadCTE.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppSim/src/ppSimBadCTE.c	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/ppSim/src/ppSimBadCTE.c	(revision 23594)
@@ -0,0 +1,34 @@
+# include "ppSim.h"
+
+// Add a "bad CTE" region to the image : it is not really modeled as bad CTE, rather it is
+// simply smoothed by a Gaussian kernel.  This has the same effect on the image variance as bad
+// CTE, but not (quite) the same effect on stellar photometry
+
+bool ppSimBadCTE(psImage *image,	// Signal image, modified and returned
+		 const pmConfig *config // configuration
+  )
+{
+    assert(image->type.type == PS_TYPE_F32);
+
+    bool status;
+
+    psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, PPSIM_RECIPE); // Recipe
+    if (!psMetadataLookupBool (&status, recipe, "BADCTE")) {
+	return true;
+    }
+    
+    char *region = psMetadataLookupStr (&status, recipe, "BADCTE.REGION");
+    psRegion badCTE = psRegionForImage (image, psRegionFromString (region));
+    if (psRegionIsNaN (badCTE)) psAbort("analysis region mis-defined");
+
+    float sigma = psMetadataLookupF32 (&status, recipe, "BADCTE.SIGMA");
+    int Nsigma = psMetadataLookupS32 (&status, recipe, "BADCTE.NSIGMA");
+
+    psImage *subset = psImageSubset (image, badCTE);
+    if (!subset) psAbort ("error in badCTE region?");
+
+    psImageSmooth (subset, sigma, Nsigma);
+
+    return true;
+}
+
Index: /branches/cnb_branches/cnb_branch_20090301/ppSim/src/ppSimLoadSpots.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppSim/src/ppSimLoadSpots.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ppSim/src/ppSimLoadSpots.c	(revision 23594)
@@ -57,5 +57,5 @@
 
     // load refstars from the catalog
-    psArray *spots = psastroLoadRefstars(config);
+    psArray *spots = psastroLoadRefstars(config, "PSASTRO.INPUT");
     if (!spots || spots->n == 0) {
         psError(PS_ERR_UNKNOWN, false, "Unable to find reference stars.");
Index: /branches/cnb_branches/cnb_branch_20090301/ppSim/src/ppSimLoadStars.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppSim/src/ppSimLoadStars.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ppSim/src/ppSimLoadStars.c	(revision 23594)
@@ -41,5 +41,5 @@
     psMetadataAdd(astroRecipe, PS_LIST_TAIL, "DEC_MIN", PS_DATA_F32 | PS_META_REPLACE, "", dec0 - radius);
     psMetadataAdd(astroRecipe, PS_LIST_TAIL, "DEC_MAX", PS_DATA_F32 | PS_META_REPLACE, "", dec0 + radius);
-    psArray *refStars = psastroLoadRefstars(config);
+    psArray *refStars = psastroLoadRefstars(config, "PSASTRO.INPUT");
     if (!refStars) {
         psError(PS_ERR_UNKNOWN, false, "Unable to find reference stars.");
Index: /branches/cnb_branches/cnb_branch_20090301/ppSim/src/ppSimLoop.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppSim/src/ppSimLoop.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ppSim/src/ppSimLoop.c	(revision 23594)
@@ -154,4 +154,7 @@
             done:
                 if (!ppSimAddNoise(readout->image, readout->variance, cell, config, rng)) ESCAPE (PS_ERR_UNKNOWN, "problem adding noise");
+
+                if (!ppSimBadCTE(readout->image, config)) ESCAPE (PS_ERR_UNKNOWN, "problem inducing bad cte");
+
                 if (!ppSimSaturate(readout, config)) ESCAPE (PS_ERR_UNKNOWN, "problem setting saturation levels");
 
Index: /branches/cnb_branches/cnb_branch_20090301/ppSim/src/ppSimRandomGaussian.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppSim/src/ppSimRandomGaussian.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ppSim/src/ppSimRandomGaussian.c	(revision 23594)
@@ -29,11 +29,11 @@
 
 /* integrate a gaussian from -5 sigma to +5 sigma */
-void p_ppSimRandomGaussianInit () {
- 
+void p_ppSimRandomGaussianInit (void) {
+
   int i;
   long A, B;
   double val, x, dx, dx1, dx2, dx3, df;
   double mean, sigma;
- 
+
   /* no need to generate this if it already exists */
   if (gaussint) return;
@@ -42,5 +42,5 @@
   for (B = 0; A == time(NULL); B++);
   srand48(B);
- 
+
   Ngaussint = 0x1000;
   ppSimRandomGaussianAlloc (Ngaussint + 1);
@@ -53,9 +53,9 @@
   mean = 0.0;
   sigma = 1.0;
- 
+
   for (i = 0, x = -7.0; (i < Ngaussint) && (x < 7.0); x += dx)  {
-    df = (3.0*p_ppSimGaussian(x    , mean, sigma) + 
+    df = (3.0*p_ppSimGaussian(x    , mean, sigma) +
           9.0*p_ppSimGaussian(x+dx1, mean, sigma) +
-          9.0*p_ppSimGaussian(x+dx2, mean, sigma) + 
+          9.0*p_ppSimGaussian(x+dx2, mean, sigma) +
           3.0*p_ppSimGaussian(x+dx3, mean, sigma)) * (dx1/8.0);
     val += df;
@@ -67,10 +67,10 @@
 }
 
-// XXX we are using drand48() rather than the random var supplied by rnd 
+// XXX we are using drand48() rather than the random var supplied by rnd
 double ppSimRandomGaussian (const psRandom *rnd, double mean, double sigma) {
- 
+
   int i;
   double y;
- 
+
   if (gaussint == NULL) {
       p_ppSimRandomGaussianInit ();
@@ -80,15 +80,15 @@
   i = Ngaussint*y;
   y = gaussint[i]*sigma + mean;
- 
+
   return (y);
- 
+
 }
- 
-// XXX we are using drand48() rather than the random var supplied by rnd 
+
+// XXX we are using drand48() rather than the random var supplied by rnd
 double ppSimRandomGaussianNorm (const psRandom *rnd) {
- 
+
   int i;
   double y;
- 
+
   if (gaussint == NULL) {
       p_ppSimRandomGaussianInit ();
@@ -98,5 +98,5 @@
   i = Ngaussint*y;
   y = gaussint[i];
- 
+
   return (y);
 }
Index: /branches/cnb_branches/cnb_branch_20090301/ppSim/src/ppSimStars.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppSim/src/ppSimStars.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ppSim/src/ppSimStars.c	(revision 23594)
@@ -6,5 +6,5 @@
 }
 
-ppSimStar *ppSimStarAlloc () {
+ppSimStar *ppSimStarAlloc(void) {
 
     ppSimStar *star = (ppSimStar *) psAlloc(sizeof(ppSimStar));
@@ -19,5 +19,5 @@
 }
 
-ppSimGalaxy *ppSimGalaxyAlloc () {
+ppSimGalaxy *ppSimGalaxyAlloc(void) {
 
     ppSimGalaxy *galaxy = (ppSimGalaxy *) psAlloc(sizeof(ppSimGalaxy));
Index: /branches/cnb_branches/cnb_branch_20090301/ppStack/src/Makefile.am
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppStack/src/Makefile.am	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ppStack/src/Makefile.am	(revision 23594)
@@ -29,4 +29,5 @@
 	ppStackPrepare.c	\
 	ppStackConvolve.c	\
+	ppStackCombinePrepare.c	\
 	ppStackCombineInitial.c	\
 	ppStackReject.c		\
Index: /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStack.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStack.h	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStack.h	(revision 23594)
@@ -7,4 +7,6 @@
 #include <pslib.h>
 #include <psmodules.h>
+
+#include "ppStackOptions.h"
 
 // Mask values for inputs
@@ -106,11 +108,6 @@
 /// Convolve image to match specified seeing
 bool ppStackMatch(pmReadout *readout,   // Readout to be convolved; replaced with output
-                  psArray **regions,    // Array of regions used in each PSF matching, returned
-                  psArray **kernels,    // Array of kernels used in each PSF matching, returned
-                  float *chi2,          // Chi^2 from the stamps
-                  float *weighting,     // Stack weighting (1/noise^2)
-                  psArray *sources,     // Array of sources
-                  const pmPSF *psf,     // Target PSF
-                  psRandom *rng,        // Random number generator
+                  ppStackOptions *options, // Options for stacking
+                  int index,            // Index of image to match
                   const pmConfig *config // Configuration
     );
@@ -120,8 +117,7 @@
 ///
 /// 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
+bool ppStackSourcesTransparency(ppStackOptions *options, // Stacking options
+                                const pmFPAview *view, // View to readout
+                                const pmConfig *config // Configuration
     );
 
Index: /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackArguments.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackArguments.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackArguments.c	(revision 23594)
@@ -20,5 +20,5 @@
     fprintf(stderr, "\nPan-STARRS Image combination\n\n");
     fprintf(stderr,
-            "Usage: %s INPUTS.mdc OUTPUT_ROOT [-sources STAMPS.cmf | -stamps STAMPS.dat]\n"
+            "Usage: %s -input INPUTS.mdc OUTPUT_ROOT [-sources STAMPS.cmf | -stamps STAMPS.dat]\n"
             "where INPUTS.mdc contains various METADATAs, each with:\n"
             "\tIMAGE(STR):     Image filename\n"
@@ -26,6 +26,5 @@
             "\tVARIANCE(STR):  Variance map filename\n"
             "\tPSF(STR):       PSF filename\n"
-            "\tSOURCES(STR):   Sources filename\n"
-            "\tWEIGHTING(F32): Relative weighting to be applied\n",
+            "\tSOURCES(STR):   Sources filename\n",
             program);
     fprintf(stderr, "\n");
@@ -137,5 +136,4 @@
         psArgumentRemove(argNum, &argc, argv);
     }
-
 
     psMetadata *arguments = config->arguments; // Command-line arguments
@@ -187,20 +185,34 @@
     psMetadataAddBool(arguments, PS_LIST_TAIL, "-visual", 0, "visualisation", false);
 
-    if (argc == 1 || !psArgumentParse(arguments, &argc, argv) || argc != 3) {
+    if (argc == 1) {
         usage(argv[0], arguments, config);
     }
+
+    if ((argNum = psArgumentGet(argc, argv, "-input"))) {
+        psArgumentRemove(argNum, &argc, argv);
+        if (argNum >= argc) {
+            usage(argv[0], arguments, config);
+        }
+
+        unsigned int numBad = 0;                     // Number of bad lines
+        psMetadata *inputs = psMetadataConfigRead(NULL, &numBad, argv[argNum], false); // Input file info
+        if (!inputs || numBad > 0) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, false, "Unable to cleanly read MDC file with inputs.");
+            return false;
+        }
+        psMetadataAddMetadata(arguments, PS_LIST_TAIL, "INPUTS", 0, "Metadata with input details", inputs);
+        psFree(inputs);
+
+        psArgumentRemove(argNum, &argc, argv);
+    }
+
+    if (!psArgumentParse(arguments, &argc, argv) || argc != 2) {
+        usage(argv[0], arguments, config);
+    }
+
+    psMetadataAddStr(arguments, PS_LIST_TAIL, "OUTPUT", 0, "Root name of the output image list", argv[1]);
 
     const char *stampsName = psMetadataLookupStr(NULL, arguments, "-stamps"); // Name of stamps file
     psMetadataAddStr(arguments, PS_LIST_TAIL, "STAMPS", 0, "Stamps file", stampsName);
-
-    unsigned int numBad = 0;                     // Number of bad lines
-    psMetadata *inputs = psMetadataConfigRead(NULL, &numBad, argv[1], false); // Information about inputs
-    if (!inputs || numBad > 0) {
-        psError(PS_ERR_BAD_PARAMETER_VALUE, false, "Unable to cleanly read MDC file with inputs.");
-        return false;
-    }
-    psMetadataAddMetadata(arguments, PS_LIST_TAIL, "INPUTS", 0, "Metadata with input details", inputs);
-    psFree(inputs);
-    psMetadataAddStr(arguments, PS_LIST_TAIL, "OUTPUT", 0, "Root name of the output image list", argv[2]);
 
     valueArgStr(arguments, "-stats", "STATS", arguments);
@@ -295,14 +307,6 @@
     psTrace("ppStack", 1, "Done parsing arguments\n");
 
-    // Dump configuration, now that's it's settled
-    bool status;
-    psString dump_file =  psMetadataLookupStr(&status, config->arguments, "DUMP_CONFIG");
-    if (dump_file) {
-        pmConfigCamerasCull(config, NULL);
-        pmConfigRecipesCull(config, "PPSTACK,PPSUB,PPSTATS,PSPHOT,MASKS,JPEG");
-
-        pmFPAfile *input = psMetadataLookupPtr(NULL, config->files, "PPSTACK.INPUT"); // Input file
-        pmConfigDump(config, input->fpa, dump_file);
-    }
+    pmConfigCamerasCull(config, NULL);
+    pmConfigRecipesCull(config, "PPSTACK,PPSUB,PPSTATS,PSPHOT,MASKS,JPEG");
 
     return true;
Index: /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackCamera.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackCamera.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackCamera.c	(revision 23594)
@@ -11,20 +11,28 @@
 #include "ppStack.h"
 
-
-#if 0
-// Define an output convolved image file
-static pmFPAfile *defineOutputConvolved(const char *name, // FPA file name
-                                        pmFPA *fpa, // FPA to bind
-                                        const pmConfig *config, // Configuration
-                                        pmFPAfileType type // Expected type
-    )
+// Define a file
+static pmFPAfile *defineFile(pmConfig *config, // Configuration
+                             pmFPAfile *bind, // File to which to bind
+                             const char *name, // Name of file rule
+                             const char *filename, // Name of file
+                             pmFPAfileType type // Type of file
+                             )
 {
-    pmFPAfile *file = pmFPAfileDefineOutput(config, fpa, name);
-    if (!file) {
-        psError(PS_ERR_UNKNOWN, false, "Unable to define output convolved file %s", name);
+
+    psArray *dummy = psArrayAlloc(1);   // Dummy array of filenames for this FPA
+    dummy->data[0] = psStringCopy(filename);
+    psMetadataAddArray(config->arguments, PS_LIST_TAIL, "FILENAMES", PS_META_REPLACE,
+                       "Filenames for file rule definition", dummy);
+    psFree(dummy);
+
+    bool found = false;             // Found the file?
+    pmFPAfile *file = bind ? pmFPAfileBindFromArgs(&found, bind, config, name, "FILENAMES") :
+        pmFPAfileDefineFromArgs(&found, config, name, "FILENAMES");
+    if (!file || !found) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to define file %s from %s", name, filename);
         return NULL;
     }
-    if (file->type != PM_FPA_FILE_IMAGE) {
-        psError(PS_ERR_IO, true, "PPSTACK.OUTCONV is not of type %s", pmFPAfileStringFromType(type));
+    if (file->type != type) {
+        psError(PS_ERR_IO, true, "%s is not of type %s", name, pmFPAfileStringFromType(type));
         return NULL;
     }
@@ -33,228 +41,189 @@
 }
 
-// Define an input convolved image file, using the output as a basis
-static pmFPAfile *defineInputConvolved(const char *inputName, // Input FPA file name
-                                       pmFPAfile *outFile, // Corresponding output FPA file
-                                       pmConfig *config, // Configuration
-                                       pmFPAfileType type // Expected type
-    )
-{
-    pmFPAview *view = pmFPAviewAlloc(0); // View into sky cells
-    view->chip = view->cell = view->readout = 0;
-
-    psString imageName = pmFPANameFromRule(outFile->filerule, outFile->fpa, view);
-    psArray *imageNames = psArrayAlloc(1);
-    imageNames->data[0] = imageName;
-    psMetadataAddArray(config->arguments, PS_LIST_TAIL, "INCONV.FILENAMES", PS_META_REPLACE,
-                       "Filenames of input convolved image files", imageNames);
-    psFree(imageNames);
-    bool found = false;                 // Found the file?
-    pmFPAfile *imageFile = pmFPAfileDefineFromArgs(&found, config, "PPSTACK.INCONV",
-                                                   "INCONV.FILENAMES");
-    psMetadataRemoveKey(config->arguments, "INCONV.FILENAMES");
-    if (!imageFile || !found) {
-        psError(PS_ERR_UNKNOWN, false, "Unable to define %s file", inputName);
-        return NULL;
-    }
-    if (imageFile->type != type) {
-        psError(PS_ERR_IO, true, "PPSTACK.INCONV is not of type %s",
-                pmFPAfileStringFromType(type));
-        return NULL;
-    }
-
-    return imageFile;
-}
-#endif
-
 
 
 bool ppStackCamera(pmConfig *config)
 {
-    bool haveVariances = false;           // Do we have variance maps?
+    int num = 0;                        // Number of inputs
+    bool haveVariances = false;         // Do we have variance maps?
     bool havePSFs = false;              // Do we have PSFs?
 
-    psMetadata *inputs = psMetadataLookupMetadata(NULL, config->arguments, "INPUTS"); // The inputs info
-    psMetadataIterator *iter = psMetadataIteratorAlloc(inputs, PS_LIST_HEAD, NULL); // Iterator
-    psMetadataItem *item;               // Item from iteration
-    int i = 0;                          // Counter
-    while ((item = psMetadataGetAndIncrement(iter))) {
-        if (item->type != PS_DATA_METADATA) {
-            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
-                    "Component %s of the input metadata is not of type METADATA", item->name);
-            psFree(iter);
-            return false;
-        }
-
-        psMetadata *input = item->data.md; // The input metadata of interest
-
-        psString image = psMetadataLookupStr(NULL, input, "IMAGE"); // Name of image
-        if (!image || strlen(image) == 0) {
-            psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Component %s lacks IMAGE of type STR", item->name);
-            psFree(iter);
-            return false;
-        }
-
-        bool mdok;
-        psString mask = psMetadataLookupStr(&mdok, input, "MASK"); // Name of mask
-        psString variance = psMetadataLookupStr(&mdok, input, "VARIANCE"); // Name of variance map
-        psString psf = psMetadataLookupStr(&mdok, input, "PSF"); // Name of PSF
-        psString sources = psMetadataLookupStr(&mdok, input, "SOURCES"); // Name of sources
-
-        // Add the image file
-        psArray *imageFiles = psArrayAlloc(1); // Array of filenames for this FPA
-        imageFiles->data[0] = psMemIncrRefCounter(image);
-        psMetadataAddArray(config->arguments, PS_LIST_TAIL, "IMAGE.FILENAMES", PS_META_REPLACE,
-                           "Filenames of image files", imageFiles);
-        psFree(imageFiles);
-
-        bool found = false;             // Found the file?
-        pmFPAfile *imageFile = pmFPAfileDefineFromArgs(&found, config, "PPSTACK.INPUT", "IMAGE.FILENAMES");
-        if (!imageFile || !found) {
-            psError(PS_ERR_UNKNOWN, false, "Unable to define file from image %d (%s)", i, image);
-            return false;
-        }
-        if (imageFile->type != PM_FPA_FILE_IMAGE) {
-            psError(PS_ERR_IO, true, "PPSTACK.INPUT is not of type IMAGE");
-            return false;
-        }
-
-        // Optionally add the mask file
-        if (mask && strlen(mask) > 0) {
-            psArray *maskFiles = psArrayAlloc(1); // Array of filenames for this FPA
-            maskFiles->data[0] = psMemIncrRefCounter(mask);
-            psMetadataAddArray(config->arguments, PS_LIST_TAIL, "MASK.FILENAMES", PS_META_REPLACE,
-                               "Filenames of mask files", maskFiles);
-            psFree(maskFiles);
-
-            bool status;
-            pmFPAfile *maskFile = pmFPAfileBindFromArgs(&status, imageFile, config, "PPSTACK.INPUT.MASK",
-                                                        "MASK.FILENAMES");
+    bool status = false;                // Status of file definition
+
+    psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, PPSTACK_RECIPE); // Recipe for ppSim
+    if (!recipe) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to find recipe %s", PPSTACK_RECIPE);
+        return false;
+    }
+    bool convolve = psMetadataLookupBool(NULL, recipe, "CONVOLVE"); // Convolve images before stack?
+
+    psArray *runImages = pmFPAfileDefineMultipleFromRun(&status, NULL, config,
+                                                        "PPSTACK.INPUT"); // Input images from previous run
+    if (runImages) {
+        // Defining files from the RUN metadata
+        num = runImages->n;
+
+        psArray *runMasks = pmFPAfileDefineMultipleFromRun(&status, runImages, config,
+                                                           "PPSTACK.INPUT.MASK"); // Input masks
+        if (!status) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to define input masks from RUN metadata.");
+            psFree(runImages);
+            return false;
+        }
+        psFree(runMasks);
+
+        psArray *runVars = pmFPAfileDefineMultipleFromRun(&status, runImages, config,
+                                                          "PPSTACK.INPUT.VARIANCE"); // Input variances
+        if (!status) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to define input variances from RUN metadata.");
+            psFree(runImages);
+            return false;
+        }
+        if (runVars) {
+            haveVariances = true;
+        }
+        psFree(runVars);
+
+        psArray *runPSF = pmFPAfileDefineMultipleFromRun(&status, runImages, config,
+                                                         "PPSTACK.INPUT.PSF"); // Input PSFs
+        if (!status) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to define input PSFs from RUN metadata.");
+            psFree(runImages);
+            return false;
+        }
+        if (runPSF) {
+            havePSFs = true;
+        }
+        psFree(runPSF);
+
+        psArray *runSrc = pmFPAfileDefineMultipleFromRun(&status, runImages, config,
+                                                         "PPSTACK.INPUT.SOURCES"); // Input sources
+        if (!status) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to define input sources from RUN metadata.");
+            psFree(runImages);
+            return false;
+        }
+        if (!runSrc) {
+            psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to define input sources from RUN metadata.");
+            psFree(runImages);
+            return false;
+        }
+        psFree(runSrc);
+
+        if (convolve) {
+            psArray *runKernel = pmFPAfileDefineMultipleFromRun(&status, runImages, config,
+                                                                "PPSTACK.CONV.KERNEL"); // Convolution kernels
             if (!status) {
+                psError(PS_ERR_UNKNOWN, false, "Unable to define convolution kernels from RUN metadata.");
+                psFree(runImages);
+                return false;
+            }
+            if (!runKernel) {
+                psError(PS_ERR_UNEXPECTED_NULL, true,
+                        "Unable to define convolution kernels from RUN metadata.");
+                psFree(runImages);
+                return false;
+            }
+            psFree(runKernel);
+        }
+
+        psFree(runImages);
+    } else {
+        // Defining files from the input metadata
+        psMetadata *inputs = psMetadataLookupMetadata(NULL, config->arguments, "INPUTS"); // The inputs info
+        if (!inputs) {
+            psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to find inputs.");
+            return false;
+        }
+        psMetadataIterator *iter = psMetadataIteratorAlloc(inputs, PS_LIST_HEAD, NULL); // Iterator
+        psMetadataItem *item;               // Item from iteration
+        int i = 0;                          // Counter
+        while ((item = psMetadataGetAndIncrement(iter))) {
+            if (item->type != PS_DATA_METADATA) {
+                psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                        "Component %s of the input metadata is not of type METADATA", item->name);
+                psFree(iter);
+                return false;
+            }
+
+            psMetadata *input = item->data.md; // The input metadata of interest
+
+            psString image = psMetadataLookupStr(NULL, input, "IMAGE"); // Name of image
+            if (!image || strlen(image) == 0) {
+                psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Component %s lacks IMAGE of type STR", item->name);
+                psFree(iter);
+                return false;
+            }
+
+            bool mdok;
+            psString mask = psMetadataLookupStr(&mdok, input, "MASK"); // Name of mask
+            psString variance = psMetadataLookupStr(&mdok, input, "VARIANCE"); // Name of variance map
+            psString psf = psMetadataLookupStr(&mdok, input, "PSF"); // Name of PSF
+            psString sources = psMetadataLookupStr(&mdok, input, "SOURCES"); // Name of sources
+
+            pmFPAfile *imageFile = defineFile(config, NULL, "PPSTACK.INPUT",
+                                              image, PM_FPA_FILE_IMAGE); // File for image
+            if (!imageFile) {
+                psError(PS_ERR_UNKNOWN, false, "Unable to define file from image %d (%s)", i, image);
+                return false;
+            }
+
+            if (mask && strlen(mask) > 0 &&
+                !defineFile(config, imageFile, "PPSTACK.INPUT.MASK", mask, PM_FPA_FILE_MASK)) {
                 psError(PS_ERR_UNKNOWN, false, "Unable to define file from mask %d (%s)", i, mask);
                 return false;
             }
-            if (maskFile->type != PM_FPA_FILE_MASK) {
-                psError(PS_ERR_IO, true, "PPSTACK.INPUT.MASK is not of type MASK");
-                return false;
-            }
-        }
-
-        // Optionally add the variance file
-        if (variance && strlen(variance) > 0) {
-            haveVariances = true;
-            psArray *varianceFiles = psArrayAlloc(1); // Array of filenames for this FPA
-            varianceFiles->data[0] = psMemIncrRefCounter(variance);
-            psMetadataAddArray(config->arguments, PS_LIST_TAIL, "VARIANCE.FILENAMES", PS_META_REPLACE,
-                               "Filenames of variance files", varianceFiles);
-            psFree(varianceFiles);
-
-            bool status;
-            pmFPAfile *varianceFile = pmFPAfileBindFromArgs(&status, imageFile, config,
-                                                            "PPSTACK.INPUT.VARIANCE", "VARIANCE.FILENAMES");
-            if (!status) {
-                psError(PS_ERR_UNKNOWN, false, "Unable to define file from variance %d (%s)", i, variance);
-                return false;
-            }
-            if (varianceFile->type != PM_FPA_FILE_VARIANCE) {
-                psError(PS_ERR_IO, true, "PPSTACK.INPUT.VARIANCE is not of type VARIANCE");
-                return false;
-            }
-        }
-
-        // Add the psf file
-        if (!psf || strlen(psf) == 0) {
-            if (havePSFs) {
-                psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find PSF %d", i);
-                return false;
-            }
-        } else {
-            if (!havePSFs && i != 0) {
-                psWarning("PSF not provided for all inputs --- ignoring.");
-            } else {
-                psArray *psfFiles = psArrayAlloc(1); // Array of filenames for this FPA
-                psfFiles->data[0] = psMemIncrRefCounter(psf);
-                psMetadataAddArray(config->arguments, PS_LIST_TAIL, "PSF.FILENAMES", PS_META_REPLACE,
-                                   "Filenames of PSF files", psfFiles);
-                psFree(psfFiles);
-
-                bool status;
-                pmFPAfile *psfFile = pmFPAfileBindFromArgs(&status, imageFile, config, "PPSTACK.INPUT.PSF",
-                                                           "PSF.FILENAMES");
-                if (!status) {
-                    psError(PS_ERR_UNKNOWN, false, "Unable to define file from psf %d (%s)", i, psf);
+
+            if (variance && strlen(variance) > 0) {
+                haveVariances = true;
+                if (!defineFile(config, imageFile, "PPSTACK.INPUT.VARIANCE", variance,
+                                PM_FPA_FILE_VARIANCE)) {
+                    psError(PS_ERR_UNKNOWN, false,
+                            "Unable to define file from variance %d (%s)", i, variance);
                     return false;
                 }
-                if (psfFile->type != PM_FPA_FILE_PSF) {
-                    psError(PS_ERR_IO, true, "PPSTACK.INPUT.PSF is not of type PSF");
+            }
+
+            if (psf && strlen(psf) > 0) {
+                if (i != 0 && !havePSFs) {
+                    psWarning("PSF not provided for all inputs --- ignoring.");
+                } else {
+                    havePSFs = true;
+                    if (!defineFile(config, imageFile, "PPSTACK.INPUT.PSF", psf, PM_FPA_FILE_PSF)) {
+                        psError(PS_ERR_UNKNOWN, false, "Unable to define file from psf %d (%s)", i, psf);
+                        return false;
+                    }
+                }
+            } else if (havePSFs) {
+                psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find PSF %d", i);
+                return false;
+            }
+
+            if (!sources || strlen(sources) == 0) {
+                psError(PS_ERR_UNEXPECTED_NULL, true, "SOURCES not provided for file %d", i);
+                return false;
+            }
+            if (!defineFile(config, imageFile, "PPSTACK.INPUT.SOURCES", sources, PM_FPA_FILE_CMF)) {
+                psError(PS_ERR_UNKNOWN, false, "Unable to define file from sources %d (%s)",
+                        i, sources);
+                return false;
+            }
+
+            if (convolve) {
+                pmFPAfile *kernel = pmFPAfileDefineOutput(config, imageFile->fpa, "PPSTACK.CONV.KERNEL");
+                if (!kernel) {
+                    psError(PS_ERR_IO, false, _("Unable to generate output file from PPSTACK.CONV.KERNEL"));
                     return false;
                 }
-                havePSFs = true;
-            }
-        }
-
-        // Add the sources file
-        {
-            psArray *sourcesFiles = psArrayAlloc(1); // Array of filenames for this FPA
-            sourcesFiles->data[0] = psMemIncrRefCounter(sources);
-            psMetadataAddArray(config->arguments, PS_LIST_TAIL, "SOURCES.FILENAMES", PS_META_REPLACE,
-                               "Filenames of SOURCES files", sourcesFiles);
-            psFree(sourcesFiles);
-
-            bool status;
-            pmFPAfile *sourcesFile = pmFPAfileBindFromArgs(&status, imageFile, config,
-                                                           "PPSTACK.INPUT.SOURCES", "SOURCES.FILENAMES");
-            if (!status) {
-                psError(PS_ERR_UNKNOWN, false, "Unable to define file from sources %d (%s)",
-                        i, sources);
-                return false;
-            }
-            if (sourcesFile->type != PM_FPA_FILE_CMF) {
-                psError(PS_ERR_IO, true, "PPSTACK.INPUT.SOURCES is not of type CMF");
-                return false;
-            }
-        }
-
-#if 0
-        // Output convolved files
-        pmFPAfile *outconvImage    = defineOutputConvolved("PPSTACK.OUTCONV", imageFile->fpa, config,
-                                                           PM_FPA_FILE_IMAGE);
-        pmFPAfile *outconvMask     = defineOutputConvolved("PPSTACK.OUTCONV.MASK", imageFile->fpa, config,
-                                                           PM_FPA_FILE_MASK);
-        pmFPAfile *outconvVariance = defineOutputConvolved("PPSTACK.OUTCONV.VARIANCE", imageFile->fpa, config,
-                                                           PM_FPA_FILE_VARIANCE);
-        if (!outconvImage || !outconvMask || !outconvVariance) {
-            return false;
-        }
-
-        // Input convolved files
-        pmFPAfile *inconvImage    = defineInputConvolved("PPSTACK.INCONV", outconvImage, config,
-                                                         PM_FPA_FILE_IMAGE);
-        pmFPAfile *inconvMask     = defineInputConvolved("PPSTACK.INCONV.MASK", outconvMask, config,
-                                                         PM_FPA_FILE_MASK);
-        pmFPAfile *inconvVariance = defineInputConvolved("PPSTACK.INCONV.VARIANCE", outconvVariance, config,
-                                                         PM_FPA_FILE_VARIANCE);
-        if (!inconvImage || !inconvMask || !inconvVariance) {
-            return false;
-        }
-#endif
-
-        i++;
-    }
-    psFree(iter);
-    psMetadataRemoveKey(config->arguments, "IMAGE.FILENAMES");
-    if (psMetadataLookup(config->arguments, "MASK.FILENAMES")) {
-        psMetadataRemoveKey(config->arguments, "MASK.FILENAMES");
-    }
-    if (psMetadataLookup(config->arguments, "VARIANCE.FILENAMES")) {
-        psMetadataRemoveKey(config->arguments, "VARIANCE.FILENAMES");
-    }
-    if (psMetadataLookup(config->arguments, "PSF.FILENAMES")) {
-        psMetadataRemoveKey(config->arguments, "PSF.FILENAMES");
-    }
-    if (psMetadataLookup(config->arguments, "SOURCES.FILENAMES")) {
-        psMetadataRemoveKey(config->arguments, "SOURCES.FILENAMES");
-    }
-
-    psMetadataAddS32(config->arguments, PS_LIST_TAIL, "INPUTS.NUM", 0, "Number of input files", i);
+                kernel->save = true;
+            }
+
+            i++;
+        }
+        psFree(iter);
+        psMetadataRemoveKey(config->arguments, "FILENAMES");
+        num = i;
+    }
+    psMetadataAddS32(config->arguments, PS_LIST_TAIL, "INPUTS.NUM", 0, "Number of input files", num);
     psMetadataAddBool(config->arguments, PS_LIST_TAIL, "HAVE.PSF", 0, "Have PSFs available?", havePSFs);
 
@@ -343,11 +312,4 @@
     }
     jpeg2->save = true;
-
-
-    psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, PPSTACK_RECIPE); // Recipe for ppSim
-    if (!recipe) {
-        psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to find recipe %s", PPSTACK_RECIPE);
-        return false;
-    }
 
     // For photometry, we operate on the chip-mosaicked image
Index: /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackCombineInitial.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackCombineInitial.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackCombineInitial.c	(revision 23594)
@@ -16,8 +16,10 @@
     psAssert(config, "Require configuration");
 
-    psTimerStart("PPSTACK_FINAL");
+    if (!options->convolve) {
+        // No need to do initial combination when we haven't convolved
+        return true;
+    }
 
-    psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, PPSTACK_RECIPE); // ppStack recipe
-    psAssert(recipe, "We've thrown an error on this before.");
+    psTimerStart("PPSTACK_INITIAL");
 
     psMetadata *ppsub = psMetadataLookupMetadata(NULL, config->recipes, "PPSUB"); // PPSUB recipe
@@ -25,33 +27,4 @@
                                           "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
@@ -69,5 +42,4 @@
         }
 
-        // call: ppStackReadoutInitial(config, outRO, readouts, subRegions, subKernels)
         psThreadJob *job = psThreadJobAlloc("PPSTACK_INITIAL_COMBINE"); // Job to start
         psArrayAdd(job->args, 1, thread);
@@ -128,6 +100,6 @@
 
     if (options->stats) {
-        psMetadataAddF32(options->stats, PS_LIST_TAIL, "TIME_FINAL", 0,
-                         "Time to make final stack", psTimerMark("PPSTACK_FINAL"));
+        psMetadataAddF32(options->stats, PS_LIST_TAIL, "TIME_INITIAL", 0,
+                         "Time to make final stack", psTimerMark("PPSTACK_INITIAL"));
     }
 
Index: /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackCombinePrepare.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackCombinePrepare.c	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackCombinePrepare.c	(revision 23594)
@@ -0,0 +1,47 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <pslib.h>
+#include <psmodules.h>
+
+#include "ppStack.h"
+#include "ppStackLoop.h"
+
+bool ppStackCombinePrepare(ppStackThreadData *stack, ppStackOptions *options, pmConfig *config)
+{
+    psAssert(stack, "Require stack");
+    psAssert(options, "Require options");
+    psAssert(config, "Require configuration");
+
+    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);
+    pmFPAview *view = ppStackFilesIterateDown(config); // View to readout
+    if (!view) {
+        return false;
+    }
+
+    pmCell *outCell = pmFPAfileThisCell(config->files, view, "PPSTACK.OUTPUT"); // Output cell
+    options->outRO = pmReadoutAlloc(outCell); // Output readout
+    psFree(view);
+
+    psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, PPSTACK_RECIPE); // ppStack recipe
+    psAssert(recipe, "We've thrown an error on this before.");
+    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;
+    }
+
+    return true;
+}
Index: /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackConvolve.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackConvolve.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackConvolve.c	(revision 23594)
@@ -30,6 +30,6 @@
     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
+    options->kernels = psArrayAlloc(num); // PSF-matching kernels --- required in the stacking
+    options->regions = psArrayAlloc(num); // PSF-matching regions --- required in the stacking
     int numGood = 0;                    // Number of good frames
     options->numCols = 0;
@@ -38,5 +38,5 @@
     psVectorInit(options->matchChi2, NAN);
     options->weightings = psVectorAlloc(num, PS_TYPE_F32); // Combination weightings for images (1/noise^2)
-    psVectorInit(options->weightings, NAN);
+    psVectorInit(options->weightings, 0.0);
     options->covariances = psArrayAlloc(num); // Covariance matrices
 
@@ -78,12 +78,8 @@
 
         // 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)) {
+        if (!ppStackMatch(readout, options, i, config)) {
             psErrorStackPrint(stderr, "Unable to match image %d --- ignoring.", i);
-            options->inputMask->data.PS_TYPE_VECTOR_MASK_DATA[i] = PPSTACK_MASK_MATCH;
+            options->inputMask->data.PS_TYPE_VECTOR_MASK_DATA[i] |= PPSTACK_MASK_MATCH;
             psErrorClear();
             continue;
@@ -92,45 +88,48 @@
 
         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]);
+
+            if (options->convolve) {
+                // Pull parameters out of convolution kernel
+                pmSubtractionKernels *kernels = psMetadataLookupPtr(NULL, readout->analysis,
+                                                                    PM_SUBTRACTION_ANALYSIS_KERNEL);
+                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);
+                psMetadataAddF32(options->stats, PS_LIST_TAIL, "KERNEL.DECONV", PS_META_DUPLICATE_OK,
+                                 "Deconvolution fraction for kernel", deconv);
+            }
         }
         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);
+        if (options->convolve) {
+            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);
-        }
+            {
+                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
@@ -152,4 +151,5 @@
     psFree(rng);
 
+    psFree(options->norm); options->norm = NULL;
     psFree(options->sourceLists); options->sourceLists = NULL;
     psFree(options->psf); options->psf = NULL;
@@ -183,5 +183,5 @@
 
     // Reject images out-of-hand on the basis of their match chi^2
-    {
+    if (options->convolve) {
         psVector *values = psVectorAllocEmpty(num, PS_TYPE_F32); // Values to sort
         for (int i = 0; i < num; i++) {
Index: /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackFiles.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackFiles.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackFiles.c	(revision 23594)
@@ -17,5 +17,6 @@
 
 /// Files required for the convolution
-static char *filesConvolve[] = { "PPSTACK.INPUT", "PPSTACK.INPUT.MASK", "PPSTACK.INPUT.VARIANCE", NULL };
+static char *filesConvolve[] = { "PPSTACK.INPUT", "PPSTACK.INPUT.MASK", "PPSTACK.INPUT.VARIANCE",
+                                 "PPSTACK.CONV.KERNEL", NULL };
 
 /// Output files for the combination
Index: /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackFinish.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackFinish.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackFinish.c	(revision 23594)
@@ -60,4 +60,13 @@
     }
 
+
+    // Dump configuration
+    bool mdok;                          // Status of MD lookup
+    psString dump = psMetadataLookupStr(&mdok, config->arguments, "DUMP_CONFIG"); // File for config
+    if (dump) {
+        pmFPAfile *input = psMetadataLookupPtr(NULL, config->files, "PPSTACK.INPUT"); // Input file
+        pmConfigDump(config, input->fpa, dump);
+    }
+
     return true;
 }
Index: /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackLoop.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackLoop.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackLoop.c	(revision 23594)
@@ -62,4 +62,13 @@
         return false;
     }
+    psFree(options->cells); options->cells = NULL;
+
+    // Prepare for combination
+    if (!ppStackCombinePrepare(stack, options, config)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to prepare for combination.");
+        psFree(stack);
+        psFree(options);
+        return false;
+    }
 
     // Initial combination
@@ -103,5 +112,5 @@
     // Clean up
     psTrace("ppStack", 2, "Cleaning up after combination....\n");
-    if (!ppStackCombineFinal(stack, options, config)) {
+    if (!ppStackCleanup(stack, options, config)) {
         psError(PS_ERR_UNKNOWN, false, "Unable to clean up.");
         psFree(stack);
Index: /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackLoop.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackLoop.h	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackLoop.h	(revision 23594)
@@ -34,4 +34,11 @@
     );
 
+// Prepare for combination
+bool ppStackCombinePrepare(
+    ppStackThreadData *stack,           // Stack
+    ppStackOptions *options,            // Options
+    pmConfig *config                    // Configuration
+    );
+
 // Initial combination
 bool ppStackCombineInitial(
@@ -54,4 +61,11 @@
     );
 
+// Cleanup following combination
+bool ppStackCleanup(
+    ppStackThreadData *stack,           // Stack
+    ppStackOptions *options,            // Options
+    pmConfig *config                    // Configuration
+    );
+
 // Photometry
 bool ppStackPhotometry(
Index: /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackMatch.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackMatch.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackMatch.c	(revision 23594)
@@ -163,12 +163,9 @@
 
 
-bool ppStackMatch(pmReadout *readout, psArray **regions, psArray **kernels, float *chi2, float *weighting,
-                  psArray *sources, const pmPSF *psf, psRandom *rng, const pmConfig *config)
+bool ppStackMatch(pmReadout *readout, ppStackOptions *options, int index, const pmConfig *config)
 {
     assert(readout);
-    assert(regions && !*regions);
-    assert(kernels && !*kernels);
+    assert(options);
     assert(config);
-    *weighting = 0.0;
 
     psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, PPSTACK_RECIPE); // ppStack recipe
@@ -197,81 +194,77 @@
     }
 
-    pmReadout *output = pmReadoutAlloc(NULL); // Output readout, for holding results temporarily
-
-    static int numInput = -1;            // Index of input file
-    numInput++;
+    // Match the PSF
+    if (options->convolve) {
+        pmReadout *conv = pmReadoutAlloc(NULL); // Conv readout, for holding results temporarily
 #ifdef TESTING
-    // Read previously produced kernel
-    if (psMetadataLookupBool(NULL, config->arguments, "PPSTACK.DEBUG.STACK")) {
-        const char *outName = psMetadataLookupStr(NULL, config->arguments, "OUTPUT"); // Output root
-        assert(outName);
-        // Read convolution kernel
-        psString filename = NULL;   // Output filename
-        psStringAppend(&filename, "%s.%d.kernel", outName, numInput);
-        psString resolved = pmConfigConvertFilename(filename, config, false, false); // Resolved filename
-        psFree(filename);
-        psFits *fits = psFitsOpen(resolved, "r"); // FITS file for subtraction kernel
-        psFree(resolved);
-        if (!fits || !pmReadoutReadSubtractionKernels(output, fits)) {
-            psError(PS_ERR_IO, false, "Unable to read previously produced kernel");
+        // This is a hack to use the temporary convolved images and kernel generated previously.
+        // This makes the 'matching' operation much faster, allowing debugging of the stack process easier.
+        // It implicitly assumes the output root name is the same between invocations.
+
+        // Read previously produced kernel
+        if (psMetadataLookupBool(NULL, config->arguments, "PPSTACK.DEBUG.STACK")) {
+            pmFPAfile *file = pmFPAfileSelectSingle(config->files, "PPSTACK.CONV.KERNEL", index);
+            psAssert(file, "Require file");
+
+            pmFPAview *view = pmFPAviewAlloc(0); // View to readout of interest
+            view->chip = view->cell = view->readout = 0;
+            psString filename = pmFPAfileNameFromRule(filerule->rule, file, view); // Filename of interest
+
+            // Read convolution kernel
+            psString resolved = pmConfigConvertFilename(filename, config, false, false); // Resolved filename
+            psFree(filename);
+            psFits *fits = psFitsOpen(resolved, "r"); // FITS file for subtraction kernel
+            psFree(resolved);
+            if (!fits || !pmReadoutReadSubtractionKernels(conv, fits)) {
+                psError(PS_ERR_IO, false, "Unable to read previously produced kernel");
+                psFitsClose(fits);
+                return false;
+            }
             psFitsClose(fits);
-            return false;
-        }
-        psFitsClose(fits);
-
-        // Add in variance factor
-        pmSubtractionKernels *kernels = psMetadataLookupPtr(NULL, output->analysis,
-                                                            PM_SUBTRACTION_ANALYSIS_KERNEL); // Kernels
-        float vf = pmSubtractionVarianceFactor(kernels, 0.0, 0.0, false); // Variance factor
-        psMetadataItem *vfItem = psMetadataLookup(readout->parent->concepts, "CELL.VARFACTOR");
-        if (!isfinite(vf)) {
-            vf = 1.0;
-        }
-        if (isfinite(vfItem->data.F32)) {
-            vfItem->data.F32 *= vf;
-        } else {
-            vfItem->data.F32 = vf;
-        }
-
-        // Read image, mask, variance
-        const char *tempImage = psMetadataLookupStr(NULL, recipe, "TEMP.IMAGE"); // Suffix for image
-        const char *tempMask = psMetadataLookupStr(NULL, recipe, "TEMP.MASK"); // Suffix for mask
-        const char *tempVariance = psMetadataLookupStr(NULL, recipe, "TEMP.VARIANCE"); // Suffix for variance map
-        psString imageName = NULL, maskName = NULL, varianceName = NULL; // Names for convolved images
-        psStringAppend(&imageName, "%s.%d.%s", outName, numInput, tempImage);
-        psStringAppend(&maskName, "%s.%d.%s", outName, numInput, tempMask);
-        psStringAppend(&varianceName, "%s.%d.%s", outName, numInput, tempVariance);
-
-        if (!readImage(&readout->image, imageName, config) || !readImage(&readout->mask, maskName, config) ||
-            !readImage(&readout->variance, varianceName, config)) {
-            psError(PS_ERR_IO, false, "Unable to read previously produced image.");
+
+            // Add in variance factor
+            pmSubtractionKernels *kernels = psMetadataLookupPtr(NULL, conv->analysis,
+                                                                PM_SUBTRACTION_ANALYSIS_KERNEL); // Kernels
+            float vf = pmSubtractionVarianceFactor(kernels, 0.0, 0.0, false); // Variance factor
+            psMetadataItem *vfItem = psMetadataLookup(readout->parent->concepts, "CELL.VARFACTOR");
+            if (!isfinite(vf)) {
+                vf = 1.0;
+            }
+            if (isfinite(vfItem->data.F32)) {
+                vfItem->data.F32 *= vf;
+            } else {
+                vfItem->data.F32 = vf;
+            }
+
+            if (!readImage(&readout->image, options->imageNames->data[index], config) ||
+                !readImage(&readout->mask, options->maskNames->data[index], config) ||
+                !readImage(&readout->variance, options->varianceNames->data[index], config)) {
+                psError(PS_ERR_IO, false, "Unable to read previously produced image.");
+                psFree(imageName);
+                psFree(maskName);
+                psFree(varianceName);
+                return false;
+            }
             psFree(imageName);
             psFree(maskName);
             psFree(varianceName);
-            return false;
-        }
-        psFree(imageName);
-        psFree(maskName);
-        psFree(varianceName);
-
-        psRegion *region = psMetadataLookupPtr(NULL, output->analysis,
-                                               PM_SUBTRACTION_ANALYSIS_REGION); // Convolution region
-
-        pmSubtractionAnalysis(readout->analysis, kernels, region,
-                              readout->image->numCols, readout->image->numRows);
-
-        psKernel *kernel = pmSubtractionKernel(kernels, 0.0, 0.0, false); // Convolution kernel
-        psKernel *covar = psImageCovarianceCalculate(kernel, readout->covariance); // New covariance matrix
-        psFree(readout->covariance);
-        readout->covariance = covar;
-        psFree(kernel);
-
-    } else {
-#endif
-
-        // Normal operations here
-        if (psMetadataLookupBool(&mdok, config->arguments, "HAVE.PSF")) {
-            assert(psf);
-            assert(sources);
+
+            psRegion *region = psMetadataLookupPtr(NULL, conv->analysis,
+                                                   PM_SUBTRACTION_ANALYSIS_REGION); // Convolution region
+
+            pmSubtractionAnalysis(readout->analysis, kernels, region,
+                                  readout->image->numCols, readout->image->numRows);
+
+            psKernel *kernel = pmSubtractionKernel(kernels, 0.0, 0.0, false); // Convolution kernel
+            psKernel *covar = psImageCovarianceCalculate(kernel, readout->covariance); // Covariance matrix
+            psFree(readout->covariance);
+            readout->covariance = covar;
+            psFree(kernel);
+        } else {
+#endif
+
+            // Normal operations here
+            psAssert(options->psf, "Require target PSF");
+            psAssert(options->sourceLists && options->sourceLists->data[index], "Require source list");
 
             int order = psMetadataLookupS32(NULL, ppsub, "SPATIAL.ORDER"); // Spatial polynomial order
@@ -284,6 +277,6 @@
             float rej = psMetadataLookupF32(NULL, ppsub, "REJ"); // Rejection threshold
             float sysError = psMetadataLookupF32(NULL, ppsub, "SYS"); // Relative systematic error in kernel
-            pmSubtractionKernelsType type = pmSubtractionKernelsTypeFromString(
-                psMetadataLookupStr(NULL, ppsub, "KERNEL.TYPE")); // Kernel type
+            const char *typeStr = psMetadataLookupStr(NULL, ppsub, "KERNEL.TYPE"); // Kernel type
+            pmSubtractionKernelsType type = pmSubtractionKernelsTypeFromString(typeStr); // Kernel type
             psVector *widths = psMetadataLookupPtr(NULL, ppsub, "ISIS.WIDTHS"); // ISIS Gaussian widths
             psVector *orders = psMetadataLookupPtr(NULL, ppsub, "ISIS.ORDERS"); // ISIS Polynomial orders
@@ -308,41 +301,17 @@
             }
 
-#if 0
-            // Testing the normalisation of the fake image
-            {
-                pmReadout *test = pmReadoutAlloc(NULL); // Test readout
-                psArray *sources = psArrayAlloc(1);     // Array of sources
-                pmSource *source = pmSourceAlloc();     // Source
-                sources->data[0] = source;
-                source->peak = pmPeakAlloc(500, 500, 10000, PM_PEAK_LONE);
-                source->psfMag = -13.0;
-                pmReadoutFakeFromSources(test, 1000, 1000, sources, NULL, NULL, psf, 0.1, 0, false, true);
-                float sum = 0.0;
-                for (int y = 0; y < test->image->numRows; y++) {
-                    for (int x = 0; x < test->image->numCols; x++) {
-                        sum += test->image->data.F32[y][x];
-                    }
-                }
-                fprintf(stderr, "Photometry: %f --> %f = -13.0 ???\n", sum, -2.5*log10(sum));
-
-                psFits *fits = psFitsOpen("testphot.fits", "w");
-                psFitsWriteImage(fits, NULL, test->image, 0, NULL);
-                psFitsClose(fits);
-                exit(0);
-            }
-#endif
-
             pmReadout *fake = pmReadoutAlloc(NULL); // Fake readout with target PSF
 
             // For the sake of stamps, remove nearby sources
-            psArray *stampSources = stackSourcesFilter(sources, footprint); // Filtered list of sources
+            psArray *stampSources = stackSourcesFilter(options->sourceLists->data[index],
+                                                       footprint); // Filtered list of sources
 
             if (!pmReadoutFakeFromSources(fake, readout->image->numCols, readout->image->numRows,
-                                          stampSources, NULL, NULL, psf, NAN, footprint + size,
+                                          stampSources, NULL, NULL, options->psf, NAN, footprint + size,
                                           false, true)) {
                 psError(PS_ERR_UNKNOWN, false, "Unable to generate fake image with target PSF.");
                 psFree(fake);
                 psFree(optWidths);
-                psFree(output);
+                psFree(conv);
                 return false;
             }
@@ -357,5 +326,5 @@
                 pmHDU *hdu = pmHDUFromCell(readout->parent);
                 psString name = NULL;
-                psStringAppend(&name, "fake_%03d.fits", numInput);
+                psStringAppend(&name, "fake_%03d.fits", index);
                 pmStackVisualPlotTestImage(fake->image, name);
                 psFits *fits = psFitsOpen(name, "w");
@@ -367,5 +336,5 @@
                 pmHDU *hdu = pmHDUFromCell(readout->parent);
                 psString name = NULL;
-                psStringAppend(&name, "real_%03d.fits", numInput);
+                psStringAppend(&name, "real_%03d.fits", index);
                 pmStackVisualPlotTestImage(readout->image, name);
                 psFits *fits = psFitsOpen(name, "w");
@@ -381,15 +350,31 @@
 
             // Do the image matching
-            if (!pmSubtractionMatch(output, NULL, readout, fake, footprint, stride, regionSize, spacing,
-                                    threshold, stampSources, stampsName, type, size, order, widths, orders,
-                                    inner, ringsOrder, binning, penalty, optimum, optWidths, optOrder,
-                                    optThresh, iter, rej, sysError, maskVal, maskBad, maskPoor, poorFrac,
-                                    badFrac, PM_SUBTRACTION_MODE_1)) {
-                psError(PS_ERR_UNKNOWN, false, "Unable to match images.");
-                psFree(fake);
-                psFree(optWidths);
-                psFree(stampSources);
-                psFree(output);
-                return false;
+            pmSubtractionKernels *kernel = psMetadataLookupPtr(&mdok, readout->analysis,
+                                                               PM_SUBTRACTION_ANALYSIS_KERNEL); // Conv kernel
+            if (kernel) {
+                if (!pmSubtractionMatchPrecalc(conv, NULL, readout, fake, readout->analysis,
+                                               stride, sysError, maskVal, maskBad, maskPoor,
+                                               poorFrac, badFrac)) {
+                    psError(PS_ERR_UNKNOWN, false, "Unable to convolve images.");
+                    psFree(fake);
+                    psFree(optWidths);
+                    psFree(stampSources);
+                    psFree(conv);
+                    return false;
+                }
+            } else {
+                if (!pmSubtractionMatch(conv, NULL, readout, fake, footprint, stride, regionSize, spacing,
+                                        threshold, stampSources, stampsName, type, size, order, widths,
+                                        orders, inner, ringsOrder, binning, penalty,
+                                        optimum, optWidths, optOrder, optThresh, iter, rej, sysError,
+                                        maskVal, maskBad, maskPoor, poorFrac, badFrac,
+                                        PM_SUBTRACTION_MODE_1)) {
+                    psError(PS_ERR_UNKNOWN, false, "Unable to match images.");
+                    psFree(fake);
+                    psFree(optWidths);
+                    psFree(stampSources);
+                    psFree(conv);
+                    return false;
+                }
             }
 
@@ -398,9 +383,9 @@
                 pmHDU *hdu = pmHDUFromCell(readout->parent);
                 psString name = NULL;
-                psStringAppend(&name, "conv_%03d.fits", numInput);
-                pmStackVisualPlotTestImage(output->image, name);
+                psStringAppend(&name, "conv_%03d.fits", index);
+                pmStackVisualPlotTestImage(conv->image, name);
                 psFits *fits = psFitsOpen(name, "w");
                 psFree(name);
-                psFitsWriteImage(fits, hdu->header, output->image, 0, NULL);
+                psFitsWriteImage(fits, hdu->header, conv->image, 0, NULL);
                 psFitsClose(fits);
             }
@@ -408,9 +393,9 @@
                 pmHDU *hdu = pmHDUFromCell(readout->parent);
                 psString name = NULL;
-                psStringAppend(&name, "diff_%03d.fits", numInput);
+                psStringAppend(&name, "diff_%03d.fits", index);
                 pmStackVisualPlotTestImage(fake->image, name);
                 psFits *fits = psFitsOpen(name, "w");
                 psFree(name);
-                psBinaryOp(fake->image, output->image, "-", fake->image);
+                psBinaryOp(fake->image, conv->image, "-", fake->image);
                 psFitsWriteImage(fits, hdu->header, fake->image, 0, NULL);
                 psFitsClose(fits);
@@ -428,5 +413,5 @@
             // Set the variance factor
             psMetadataItem *vfItem = psMetadataLookup(readout->parent->concepts, "CELL.VARFACTOR");
-            float vf = psMetadataLookupF32(NULL, output->analysis, PM_SUBTRACTION_ANALYSIS_VARFACTOR_1);
+            float vf = psMetadataLookupF32(NULL, conv->analysis, PM_SUBTRACTION_ANALYSIS_VARFACTOR_1);
             if (!isfinite(vf)) {
                 vf = 1.0;
@@ -443,113 +428,90 @@
             psFree(readout->variance);
             psFree(readout->covariance);
-            readout->image  = psMemIncrRefCounter(output->image);
-            readout->mask   = psMemIncrRefCounter(output->mask);
-            readout->variance = psMemIncrRefCounter(output->variance);
-            readout->covariance = psImageCovarianceTruncate(output->covariance, COVAR_FRAC);
-        } else {
-            // Fake the convolution
-            psRegion *region = psRegionAlloc(0, readout->image->numCols - 1, 0, readout->image->numRows - 1);
-            psMetadataAddPtr(output->analysis, PS_LIST_TAIL, PM_SUBTRACTION_ANALYSIS_REGION,
-                             PS_DATA_REGION | PS_META_DUPLICATE_OK, "Fake subtraction region", region);
-            psFree(region);
-            pmSubtractionKernels *kernels = pmSubtractionKernelsPOIS(FAKE_SIZE, 0, penalty,
-                                                                     PM_SUBTRACTION_MODE_1);
-            // Set solution to delta function
-            kernels->solution1 = psVectorAlloc(kernels->num + 2, PS_TYPE_F64);
-            psVectorInit(kernels->solution1, 0.0);
-            int normIndex = PM_SUBTRACTION_INDEX_NORM(kernels); // Index for normalisation
-            kernels->solution1->data.F64[normIndex] = 1.0;
-            psMetadataAddPtr(output->analysis, PS_LIST_TAIL, PM_SUBTRACTION_ANALYSIS_KERNEL,
-                             PS_DATA_UNKNOWN | PS_META_DUPLICATE_OK, "Fake subtraction kernel", kernels);
-            psFree(kernels);
-        }
-
+            readout->image  = psMemIncrRefCounter(conv->image);
+            readout->mask   = psMemIncrRefCounter(conv->mask);
+            readout->variance = psMemIncrRefCounter(conv->variance);
+            readout->covariance = psImageCovarianceTruncate(conv->covariance, COVAR_FRAC);
 #ifdef TESTING
-        // Write convolution kernel
+        }
+#endif
+
+        // Extract the regions and solutions used in the image matching
+        // This stops them from being freed when we iterate back up the FPA
+        psArray *regions = options->regions->data[index] = psArrayAllocEmpty(ARRAY_BUFFER); // Match regions
         {
-            const char *outName = psMetadataLookupStr(NULL, config->arguments, "OUTPUT"); // Output root
-            assert(outName);
-
-            psString filename = NULL;   // Output filename
-            psStringAppend(&filename, "%s.%d.kernel", outName, numInput);
-            psString resolved = pmConfigConvertFilename(filename, config, true, false); // Resolved filename
-            psFree(filename);
-            psFits *fits = psFitsOpen(resolved, "w"); // FITS file for subtraction kernel
-            psFree(resolved);
-            pmReadoutWriteSubtractionKernels(output, fits);
-            psFitsClose(fits);
-        }
-    }
-#endif
-
-    readout->analysis = psMetadataCopy(readout->analysis, output->analysis);
-
-// Extract the regions and solutions used in the image matching
-// This stops them from being freed when we iterate back up the FPA
-    *regions = psArrayAllocEmpty(ARRAY_BUFFER); // Array of regions
-    {
-        psString regex = NULL;          // Regular expression
-        psStringAppend(&regex, "^%s$", PM_SUBTRACTION_ANALYSIS_REGION);
-        psMetadataIterator *iter = psMetadataIteratorAlloc(output->analysis, PS_LIST_HEAD, regex); // Iterator
-        psFree(regex);
-        psMetadataItem *item = NULL;// Item from iteration
-        while ((item = psMetadataGetAndIncrement(iter))) {
-            assert(item->type == PS_DATA_REGION);
-            *regions = psArrayAdd(*regions, ARRAY_BUFFER, item->data.V);
-        }
-        psFree(iter);
-    }
-    *kernels = psArrayAllocEmpty(ARRAY_BUFFER); // Array of kernels
-    {
-        psString regex = NULL;          // Regular expression
-        psStringAppend(&regex, "^%s$", PM_SUBTRACTION_ANALYSIS_KERNEL);
-        psMetadataIterator *iter = psMetadataIteratorAlloc(output->analysis, PS_LIST_HEAD, regex); // Iterator
-        psFree(regex);
-        psMetadataItem *item = NULL;// Item from iteration
-        while ((item = psMetadataGetAndIncrement(iter))) {
-            assert(item->type == PS_DATA_UNKNOWN);
-            // Set the normalisation dimensions, since these will be otherwise unavailable when reading the
-            // images by scans.
-            pmSubtractionKernels *kernel = item->data.V; // Kernel used in subtraction
-            kernel->numCols = readout->image->numCols;
-            kernel->numRows = readout->image->numRows;
-
-            *kernels = psArrayAdd(*kernels, ARRAY_BUFFER, kernel);
-        }
-        psFree(iter);
-    }
-    assert((*regions)->n == (*kernels)->n);
-
-    // Record chi^2
-    {
-        *chi2 = 0.0;
-        int num = 0;                    // Number of measurements of chi^2
-        psString regex = NULL;          // Regular expression
-        psStringAppend(&regex, "^%s$", PM_SUBTRACTION_ANALYSIS_KERNEL);
-        psMetadataIterator *iter = psMetadataIteratorAlloc(output->analysis, PS_LIST_HEAD, regex); // Iterator
-        psFree(regex);
-        psMetadataItem *item = NULL;// Item from iteration
-        while ((item = psMetadataGetAndIncrement(iter))) {
-            assert(item->type == PS_DATA_UNKNOWN);
-            pmSubtractionKernels *kernels = item->data.V; // Convolution kernels
-            *chi2 += kernels->mean;
-            num++;
-        }
-        psFree(iter);
-        *chi2 /= psImageCovarianceFactor(readout->covariance) * num;
-    }
-
-    // Reject image completely if the maximum deconvolution fraction exceeds the limit
-    float deconv = psMetadataLookupF32(NULL, output->analysis,
-                                       PM_SUBTRACTION_ANALYSIS_DECONV_MAX); // Maximum deconvolution fraction
-    if (deconv > deconvLimit) {
-        psWarning("Maximum deconvolution fraction (%f) exceeds limit (%f) --- rejecting\n",
-                  deconv, deconvLimit);
-        psFree(output);
-        return NULL;
+            psString regex = NULL;          // Regular expression
+            psStringAppend(&regex, "^%s$", PM_SUBTRACTION_ANALYSIS_REGION);
+            psMetadataIterator *iter = psMetadataIteratorAlloc(conv->analysis, PS_LIST_HEAD, regex);
+            psFree(regex);
+            psMetadataItem *item = NULL;// Item from iteration
+            while ((item = psMetadataGetAndIncrement(iter))) {
+                assert(item->type == PS_DATA_REGION);
+                regions = psArrayAdd(regions, ARRAY_BUFFER, item->data.V);
+            }
+            psFree(iter);
+        }
+        psArray *kernels = options->kernels->data[index] = psArrayAllocEmpty(ARRAY_BUFFER); // Match kernels
+        {
+            psString regex = NULL;          // Regular expression
+            psStringAppend(&regex, "^%s$", PM_SUBTRACTION_ANALYSIS_KERNEL);
+            psMetadataIterator *iter = psMetadataIteratorAlloc(conv->analysis, PS_LIST_HEAD, regex);
+            psFree(regex);
+            psMetadataItem *item = NULL;// Item from iteration
+            while ((item = psMetadataGetAndIncrement(iter))) {
+                assert(item->type == PS_DATA_UNKNOWN);
+                // Set the normalisation dimensions, since these will be otherwise unavailable when reading
+                // the images by scans.
+                pmSubtractionKernels *kernel = item->data.V; // Kernel used in subtraction
+                kernel->numCols = readout->image->numCols;
+                kernel->numRows = readout->image->numRows;
+
+                kernels = psArrayAdd(kernels, ARRAY_BUFFER, kernel);
+            }
+            psFree(iter);
+        }
+        psAssert((regions)->n == (kernels)->n, "Number of match regions and kernels should match");
+
+        // Record chi^2
+        {
+            double sum = 0.0;           // Sum of chi^2
+            int num = 0;                // Number of measurements of chi^2
+            psString regex = NULL;      // Regular expression
+            psStringAppend(&regex, "^%s$", PM_SUBTRACTION_ANALYSIS_KERNEL);
+            psMetadataIterator *iter = psMetadataIteratorAlloc(conv->analysis, PS_LIST_HEAD, regex);
+            psFree(regex);
+            psMetadataItem *item = NULL;// Item from iteration
+            while ((item = psMetadataGetAndIncrement(iter))) {
+                assert(item->type == PS_DATA_UNKNOWN);
+                pmSubtractionKernels *kernels = item->data.V; // Convolution kernels
+                sum += kernels->mean;
+                num++;
+            }
+            psFree(iter);
+            options->matchChi2->data.F32[index] = sum / (psImageCovarianceFactor(readout->covariance) * num);
+        }
+
+        // Reject image completely if the maximum deconvolution fraction exceeds the limit
+        float deconv = psMetadataLookupF32(NULL, conv->analysis,
+                                           PM_SUBTRACTION_ANALYSIS_DECONV_MAX); // Max deconvolution fraction
+        if (deconv > deconvLimit) {
+            psWarning("Maximum deconvolution fraction (%f) exceeds limit (%f) --- rejecting\n",
+                      deconv, deconvLimit);
+            psFree(conv);
+            return NULL;
+        }
+
+        readout->analysis = psMetadataCopy(readout->analysis, conv->analysis);
+
+        psFree(conv);
+    } else {
+        // Match the normalisation
+        float norm = powf(10.0, -0.4 * options->norm->data.F32[index]); // Normalisation
+        psBinaryOp(readout->image, readout->image, "*", psScalarAlloc(norm, PS_TYPE_F32));
+        psBinaryOp(readout->variance, readout->variance, "*", psScalarAlloc(PS_SQR(norm), PS_TYPE_F32));
     }
 
     // Ensure the background value is zero
     psStats *bg = psStatsAlloc(PS_STAT_ROBUST_MEDIAN | PS_STAT_ROBUST_STDEV); // Statistics for background
+    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS); // Random number generator
     if (!psImageBackground(bg, NULL, readout->image, readout->mask, maskVal | maskBad, rng)) {
         psWarning("Can't measure background for image.");
@@ -565,70 +527,15 @@
     if (!psImageBackground(bg, NULL, readout->variance, readout->mask, maskVal | maskBad, rng)) {
         psError(PS_ERR_UNKNOWN, false, "Can't measure mean variance for image.");
-        psFree(output);
+        psFree(rng);
+        psFree(bg);
         return false;
     }
-    *weighting = 1.0 / (psStatsGetValue(bg, PS_STAT_ROBUST_MEDIAN) *
-                        psImageCovarianceFactor(readout->covariance));
+    options->weightings->data.F32[index] = 1.0 / (psStatsGetValue(bg, PS_STAT_ROBUST_MEDIAN) *
+                                                  psImageCovarianceFactor(readout->covariance));
     psMetadataAddF32(readout->analysis, PS_LIST_TAIL, "PPSTACK.WEIGHTING", 0,
-                     "Weighting by 1/noise^2 for stack", *weighting);
-
+                     "Weighting by 1/noise^2 for stack", options->weightings->data.F32[index]);
+
+    psFree(rng);
     psFree(bg);
-
-#if 0
-#define RADIUS 10                       // Radius of photometry
-#define MIN_ERR 0.05                    // Minimum photometric error, mag
-#define MAX_MAG -13                     // Maximum magnitude for source
-
-    // Ensure the normalisation is correct
-    // XXX Ideally, would like to do proper PSF-fit photometry, but will settle for aperture photometry
-    int numSources = sources->n;        // Number of sources
-    psVector *ratio = psVectorAlloc(numSources, PS_TYPE_F32); // Flux ratios for sources
-    psVector *ratioMask = psVectorAlloc(numSources, PS_TYPE_MASK); // Mask for flux ratios
-    psVectorInit(ratioMask, 0xFF);
-    psImage *image = readout->image;    // Image of interest
-    psImage *mask = readout->mask;      // Mask of interest
-    int numCols = image->numCols, numRows = image->numRows; // Size of image
-    for (int i = 0; i < numSources; i++) {
-        pmSource *source = sources->data[i]; // Source of interest
-        if (!source || source->mode & SOURCE_MASK || !isfinite(source->psfMag) || !isfinite(source->errMag) ||
-            source->errMag > MIN_ERR || source->psfMag > MAX_MAG) {
-            continue;
-        }
-
-        float xSrc, ySrc;              // Source coordinates
-        coordsFromSource(&xSrc, &ySrc, source);
-        int xMin = PS_MAX(0, xSrc - RADIUS), xMax = PS_MIN(numCols - 1, xSrc + RADIUS); // Bounds in x
-        int yMin = PS_MAX(0, ySrc - RADIUS), yMax = PS_MIN(numRows - 1, ySrc + RADIUS); // Bounds in y
-        int numPix = 0;                 // Number of pixels
-        float sum = 0.0;                // Sum of pixels
-        for (int y = yMin; y <= yMax; y++) {
-            for (int x = xMin; x <= xMax; x++) {
-                if (mask->data.PS_TYPE_MASK_DATA[y][x] & maskBad) {
-                    continue;
-                }
-                sum += image->data.F32[y][x];
-                numPix++;
-            }
-        }
-        if (sum >= 0 && numPix > 0) {
-            float mag = -2.5 * log10(sum * M_PI * PS_SQR(RADIUS) / numPix); // Instrumental magnitude
-            ratio->data.F32[i] = mag - source->psfMag;
-            ratioMask->data.PS_TYPE_MASK_DATA[i] = 0;
-        }
-    }
-
-    psStats *stats = psStatsAlloc(PS_STAT_ROBUST_MEDIAN | PS_STAT_ROBUST_STDEV); // Statistics
-    if (!psVectorStats(stats, ratio, NULL, ratioMask, 0xFF)) {
-        psWarning("Unable to measure normalisation --- assuming correct.");
-    } else {
-        psLogMsg("ppStack", PS_LOG_INFO, "Renormalising image by %f (+/- %f) mag\n",
-                 stats->robustMedian, stats->robustStdev);
-        float norm = powf(10.0, -0.4 * stats->robustMedian); // Normalisation to apply
-        psBinaryOp(image, image, "*", psScalarAlloc(norm, PS_TYPE_F32));
-    }
-    psFree(stats);
-    psFree(ratio);
-    psFree(ratioMask);
-#endif
 
 #ifdef TESTING
@@ -636,14 +543,12 @@
         pmHDU *hdu = pmHDUFromCell(readout->parent);
         psString name = NULL;
-        psStringAppend(&name, "convolved_%03d.fits", numInput);
-        pmStackVisualPlotTestImage(output->image, name);
+        psStringAppend(&name, "convolved_%03d.fits", index);
+        pmStackVisualPlotTestImage(readout->image, name);
         psFits *fits = psFitsOpen(name, "w");
         psFree(name);
-        psFitsWriteImage(fits, hdu->header, output->image, 0, NULL);
+        psFitsWriteImage(fits, hdu->header, readout->image, 0, NULL);
         psFitsClose(fits);
     }
 #endif
-
-    psFree(output);
 
     return true;
Index: /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackOptions.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackOptions.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackOptions.c	(revision 23594)
@@ -18,7 +18,8 @@
     psFree(options->inputMask);
     psFree(options->sourceLists);
+    psFree(options->norm);
     psFree(options->cells);
-    psFree(options->subKernels);
-    psFree(options->subRegions);
+    psFree(options->kernels);
+    psFree(options->regions);
     psFree(options->matchChi2);
     psFree(options->weightings);
@@ -35,4 +36,5 @@
     psMemSetDeallocator(options, (psFreeFunc)stackOptionsFree);
 
+    options->convolve = true;
     options->stats = NULL;
     options->statsFile = NULL;
@@ -44,7 +46,8 @@
     options->inputMask = NULL;
     options->sourceLists = NULL;
+    options->norm = NULL;
     options->cells = NULL;
-    options->subKernels = NULL;
-    options->subRegions = NULL;
+    options->kernels = NULL;
+    options->regions = NULL;
     options->numCols = 0;
     options->numRows = 0;
Index: /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackOptions.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackOptions.h	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackOptions.h	(revision 23594)
@@ -8,4 +8,5 @@
 typedef struct {
     // Setup
+    bool convolve;                      // Convolve images?
     psMetadata *stats;                  // Statistics for output
     FILE *statsFile;                    // File to which to write statistics
@@ -17,8 +18,9 @@
     psVector *inputMask;                // Mask for inputs
     psArray *sourceLists;               // Individual lists of sources for matching
+    psVector *norm;                     // Normalisation for each image
     // 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
+    psArray *kernels;                   // PSF-matching kernels --- required in the stacking
+    psArray *regions;                   // PSF-matching regions --- required in the stacking
     int numCols, numRows;               // Size of image
     psVector *matchChi2;                // chi^2 for stamps from matching
Index: /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackPhotometry.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackPhotometry.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackPhotometry.c	(revision 23594)
@@ -74,5 +74,4 @@
         return false;
     }
-    psFree(photView);
 
     if (!pmFPAfileDropInternal(config->files, "PSPHOT.BACKMDL") ||
@@ -93,4 +92,6 @@
                          "Time to do photometry", psTimerMark("PPSTACK_PHOT"));
     }
+    psFree(photView);
+
     psLogMsg("ppStack", PS_LOG_INFO, "Time to do photometry: %f sec", psTimerClear("PPSTACK_PHOT"));
 
Index: /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackPrepare.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackPrepare.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackPrepare.c	(revision 23594)
@@ -127,22 +127,22 @@
     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
+    pmFPAfileActivate(config->files, false, NULL);
+    ppStackFileActivation(config, PPSTACK_FILES_PREPARE, true);
+    pmFPAview *view = ppStackFilesIterateDown(config);
+    if (!view) {
+        return false;
+    }
+
+    psMetadataIterator *fileIter = psMetadataIteratorAlloc(config->files, PS_LIST_HEAD, "^PPSTACK.INPUT$");
+    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
+
+        // Get list of PSFs, to determine target PSF
+        if (options->convolve) {
             pmChip *chip = pmFPAviewThisChip(view, inputFile->fpa); // The chip: holds the PSF
             pmPSF *psf = psMetadataLookupPtr(NULL, chip->analysis, "PSPHOT.PSF"); // PSF
@@ -173,57 +173,51 @@
                 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;
-                }
-
+        }
+
+
+        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);
-                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
+                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);
+
+    // Generate target PSF
+    if (options->convolve) {
         options->psf = ppStackPSF(config, numCols, numRows, psfs, options->inputMask);
         psFree(psfs);
@@ -240,10 +234,16 @@
                          "Target PSF", options->psf);
         outChip->data_exists = true;
-
+    }
+
+    // Zero point calibration
+    if (!ppStackSourcesTransparency(options, view, config)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to calculate transparency differences");
         psFree(view);
-
-        if (!ppStackFilesIterateUp(config)) {
-            return false;
-        }
+        return false;
+    }
+    psFree(view);
+
+    if (!ppStackFilesIterateUp(config)) {
+        return false;
     }
 
Index: /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackReadout.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackReadout.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackReadout.c	(revision 23594)
@@ -150,5 +150,5 @@
 
     if (!pmStackCombine(outRO, stack, maskVal | maskBad, maskBad, kernelSize, iter,
-                        combineRej, combineSys, combineDiscard, true, useVariance, safe)) {
+                        combineRej, combineSys, combineDiscard, true, useVariance, safe, false)) {
         psError(PS_ERR_UNKNOWN, false, "Unable to combine input readouts with rejection.");
         psFree(stack);
@@ -185,6 +185,5 @@
     assert(outRO);
     assert(readouts);
-    assert(rejected);
-    assert(readouts->n == rejected->n);
+    assert(!rejected || readouts->n == rejected->n);
     assert(mask && mask->n == readouts->n && mask->type.type == PS_TYPE_VECTOR_MASK);
     assert(weightings && weightings->n == readouts->n && weightings->type.type == PS_TYPE_F32);
@@ -197,5 +196,10 @@
 
     bool mdok;                          // Status of MD lookup
+    int iter = psMetadataLookupS32(NULL, recipe, "ITER"); // Rejection iterations
+    float combineRej = psMetadataLookupF32(NULL, recipe, "COMBINE.REJ"); // Combination threshold
+    float combineSys = psMetadataLookupF32(NULL, recipe, "COMBINE.SYS"); // Combination systematic error
+    float combineDiscard = psMetadataLookupF32(NULL, recipe, "COMBINE.DISCARD"); // Olympic discard fraction
     bool useVariance = psMetadataLookupBool(&mdok, recipe, "VARIANCE"); // Use variance for rejection?
+    bool safe = psMetadataLookupBool(&mdok, recipe, "SAFE"); // Be safe when combining small numbers of pixels
 
     psString maskValStr = psMetadataLookupStr(NULL, recipe, "MASK.VAL"); // Name of bits to mask going in
@@ -207,10 +211,22 @@
     psArray *stack = psArrayAlloc(num); // Array for stacking
 
-    int numGood = num;                  // Number of good inputs: images that haven't been completely rejected
+    bool entire = true;                 // Combine entire image?
+    if (rejected) {
+        // We have rejection from a previous combination: combine without flagging pixels to inspect
+        entire = false;
+        safe = false;
+        iter = 0;
+        combineRej = NAN;
+        combineSys = NAN;
+    }
+
     for (int i = 0; i < num; i++) {
         pmReadout *ro = readouts->data[i];
-        if (!ro || !rejected->data[i] || mask->data.PS_TYPE_VECTOR_MASK_DATA[i]) {
-            // Image completely rejected
-            numGood--;
+        if (mask->data.U8[i] & (PPSTACK_MASK_REJECT | PPSTACK_MASK_BAD)) {
+            // Image completely rejected since previous combination
+            entire = true;
+            continue;
+        } else if (mask->data.U8[i]) {
+            // Image completely rejected before original combination
             continue;
         }
@@ -234,10 +250,11 @@
         pmStackData *data = pmStackDataAlloc(ro, weightings->data.F32[i],
                                              addVariance ? addVariance->data.F32[i] : NAN);
-        data->reject = psMemIncrRefCounter(rejected->data[i]);
+        data->reject = rejected ? psMemIncrRefCounter(rejected->data[i]) : NULL;
         stack->data[i] = data;
     }
 
-    if (!pmStackCombine(outRO, stack, maskVal | maskBad, maskBad, 0, 0, NAN, NAN, NAN,
-                        numGood != num, useVariance, false)) {
+    if (!pmStackCombine(outRO, stack, maskVal | maskBad, maskBad, 0,
+                        iter, combineRej, combineSys, combineDiscard,
+                        entire, useVariance, safe, !rejected)) {
         psError(PS_ERR_UNKNOWN, false, "Unable to combine input readouts.");
         psFree(stack);
Index: /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackReject.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackReject.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackReject.c	(revision 23594)
@@ -14,4 +14,9 @@
     psAssert(options, "Require options");
     psAssert(config, "Require configuration");
+
+    if (!options->convolve) {
+        // No need to do complicated rejection when we haven't convolved
+        return true;
+    }
 
     int num = options->num;             // Number of inputs
@@ -86,6 +91,6 @@
 
         psPixels *reject = pmStackReject(options->inspect->data[i], options->numCols, options->numRows,
-                                         threshold, poorFrac, stride, options->subRegions->data[i],
-                                         options->subKernels->data[i]); // Rejected pixels
+                                         threshold, poorFrac, stride, options->regions->data[i],
+                                         options->kernels->data[i]); // Rejected pixels
 
 #ifdef TESTING
@@ -141,6 +146,6 @@
 
     psFree(options->inspect); options->inspect = NULL;
-    psFree(options->subKernels); options->subKernels = NULL;
-    psFree(options->subRegions); options->subRegions = NULL;
+    psFree(options->kernels); options->kernels = NULL;
+    psFree(options->regions); options->regions = NULL;
 
     if (numRejected >= num - 1) {
Index: /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackSetup.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackSetup.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackSetup.c	(revision 23594)
@@ -11,4 +11,32 @@
 #include "ppStackLoop.h"
 
+#define BUFFER 16                       // Buffer for name array
+
+// Generate an array of input filenames
+static psArray *stackNameArray(const ppStackOptions *options, // Stack options
+                               pmConfig *config, // Configuration
+                               const char *name // Name of file
+    )
+{
+    psAssert(config, "Require configuration");
+    psAssert(config, "Require file name");
+
+    psArray *array = psArrayAllocEmpty(options->num); // Array with filenames
+    pmFPAview *view = pmFPAviewAlloc(0);// View to readout
+    view->chip = view->cell = view->readout = -1;
+
+    for (int i = 0; i < options->num; i++) {
+        pmFPAfile *file = pmFPAfileSelectSingle(config->files, name, i); // File of interest
+
+        psString filename = pmFPAfileName(file, view, config); // Filename of interest
+        psAssert(filename, "Can't determine filename");
+        psArrayAdd(array, array->n, filename);
+        psFree(filename);               // Drop reference
+    }
+
+    return array;
+}
+
+
 bool ppStackSetup(ppStackOptions *options, pmConfig *config)
 {
@@ -18,4 +46,10 @@
     psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, PPSTACK_RECIPE); // ppStack recipe
     psAssert(recipe, "We've thrown an error on this before.");
+
+    options->convolve = psMetadataLookupBool(NULL, config->arguments, "CONVOLVE"); // Convolve images?
+    if (!psMetadataLookupBool(NULL, config->arguments, "HAVE.PSF")) {
+        psWarning("No PSFs provided --- unable to convolve to common PSF.");
+        options->convolve = false;
+    }
 
     int num = psMetadataLookupS32(NULL, config->arguments, "INPUTS.NUM"); // Number of inputs
@@ -36,43 +70,50 @@
     }
 
-    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;
+    // Generate temporary names for convolved images
+    if (options->convolve) {
+        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 images
+        const char *tempMask = psMetadataLookupStr(NULL, recipe, "TEMP.MASK"); // Suffix for masks
+        const char *tempVariance = psMetadataLookupStr(NULL, recipe, "TEMP.VARIANCE"); // Suffix for 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);
+    } else {
+        options->imageNames = stackNameArray(options, config, "PPSTACK.INPUT");
+        options->maskNames = stackNameArray(options, config, "PPSTACK.INPUT.MASK");
+        options->varianceNames = stackNameArray(options, config, "PPSTACK.INPUT.VARIANCE");
     }
-
-    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)) {
Index: /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackSources.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackSources.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ppStack/src/ppStackSources.c	(revision 23594)
@@ -9,7 +9,9 @@
 //#define TESTING                         // Enable debugging output
 
+#ifdef TESTING
 // Size of fake image; set by hand because it's trouble to get it from other places
 #define FAKE_COLS 4861
 #define FAKE_ROWS 4913
+#endif
 
 #ifdef TESTING
@@ -53,13 +55,17 @@
 
 
-float ppStackSourcesTransparency(const psArray *sourceLists, psVector *inputMask,
-                                 const pmFPAview *view, const pmConfig *config)
+bool ppStackSourcesTransparency(ppStackOptions *options, 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);
+    PS_ASSERT_PTR_NON_NULL(options, false);
+    PS_ASSERT_PTR_NON_NULL(view, false);
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psArray *sourceLists = options->sourceLists; // Source lists for each input
+    psVector *inputMask = options->inputMask; // Mask for inputs
+
+    PS_ASSERT_ARRAY_NON_NULL(sourceLists, false);
+    PS_ASSERT_VECTOR_NON_NULL(inputMask, false);
+    PS_ASSERT_VECTOR_TYPE(inputMask, PS_TYPE_U8, false);
+    PS_ASSERT_VECTOR_SIZE(inputMask, sourceLists->n, false);
 
 #if defined(TESTING) && 0
@@ -100,5 +106,5 @@
     if (!airmassZP) {
         psError(PS_ERR_UNKNOWN, false, "Unable to find ZP.AIRMASS in recipe.");
-        return NAN;
+        return false;
     }
 
@@ -139,5 +145,5 @@
                     exptime, airmass, expFilter);
             psFree(zp);
-            return NAN;
+            return false;
         }
 
@@ -149,10 +155,10 @@
                         "Unable to find airmass term (ZP.AIRMASS) for filter %s", filter);
                 psFree(zp);
-                return NAN;
+                return false;
             }
         } else if (strcmp(filter, expFilter) != 0) {
             psError(PS_ERR_BAD_PARAMETER_VALUE, false, "Filters don't match: %s vs %s", filter, expFilter);
             psFree(zp);
-            return NAN;
+            return false;
         }
 
@@ -160,4 +166,6 @@
         sumExpTime += exptime;
     }
+
+    options->sumExposure = sumExpTime;
 
     psArray *matches = pmSourceMatchSources(sourceLists, radius, true); // List of matches
@@ -165,5 +173,5 @@
         psError(PS_ERR_UNKNOWN, false, "Unable to match sources");
         psFree(zp);
-        return NAN;
+        return false;
     }
 
@@ -177,5 +185,5 @@
     if (!trans) {
         psError(PS_ERR_UNKNOWN, false, "Unable to measure transparencies");
-        return NAN;
+        return false;
     }
 
@@ -186,5 +194,5 @@
     for (int i = 0; i < trans->n; i++) {
         if (!isfinite(trans->data.F32[i])) {
-            inputMask->data.U8[i] = PPSTACK_MASK_CAL;
+            inputMask->data.U8[i] |= PPSTACK_MASK_CAL;
         }
     }
@@ -223,4 +231,5 @@
     // m_0 = m_1 + zp_1 + trans_1 - c1 * airmass_0 - 2.5log(t_0)
     // We don't need to know the magnitude zero point for the filter, since it cancels out
+    options->norm = psVectorAlloc(num, PS_TYPE_F32);
     for (int i = 0; i < num; i++) {
         if (!isfinite(trans->data.F32[i])) {
@@ -229,4 +238,5 @@
         psArray *sources = sourceLists->data[i]; // Sources of interest
         float magCorr = airmassTerm - 2.5*log10(sumExpTime) - zp->data.F32[i] - trans->data.F32[i];
+        options->norm->data.F32[i] = magCorr;
         psLogMsg("ppStack", PS_LOG_INFO, "Applying magnitude correction to image %d: %f\n", i, magCorr);
 
@@ -248,5 +258,5 @@
             psError(PS_ERR_UNKNOWN, false, "Unable to match sources");
             psFree(zp);
-            return NAN;
+            return false;
         }
         psVector *trans = pmSourceMatchRelphot(matches, zp, iter, tol, starLimit, transIter, transRej,
@@ -259,4 +269,4 @@
 #endif
 
-    return sumExpTime;
+    return true;
 }
Index: /branches/cnb_branches/cnb_branch_20090301/ppStats/src/ppStats.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppStats/src/ppStats.h	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ppStats/src/ppStats.h	(revision 23594)
@@ -149,6 +149,6 @@
 
 // used by ppStatsFromMetadata:
-psDataType psDataTypeFromString (char *typename);
-ppStatsEntry *ppStatsEntryAlloc ();
+psDataType psDataTypeFromString(char *typename);
+ppStatsEntry *ppStatsEntryAlloc(void);
 
 psArray *ppStatsFromMetadataEntries (psMetadata *recipe);
Index: /branches/cnb_branches/cnb_branch_20090301/ppSub/src/ppSubArguments.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppSub/src/ppSubArguments.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ppSub/src/ppSubArguments.c	(revision 23594)
@@ -260,5 +260,5 @@
     }
 
-    psMetadataAddStr(config->arguments, PS_LIST_TAIL, "OUTPUT", 0, "Name of the output image", argv[3]);
+    psMetadataAddStr(config->arguments, PS_LIST_TAIL, "OUTPUT", 0, "Name of the output image", argv[1]);
 
 
@@ -282,5 +282,5 @@
     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);
+        fileList("REF", refImage, "Name of the reference image", config);
     }
     const char *refMask = psMetadataLookupStr(NULL, arguments, "-refmask"); // Name of reference mask
Index: /branches/cnb_branches/cnb_branch_20090301/ppSub/src/ppSubCamera.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppSub/src/ppSubCamera.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ppSub/src/ppSubCamera.c	(revision 23594)
@@ -33,5 +33,5 @@
 
     // look for the file on the RUN metadata
-    pmFPAfile *file = pmFPAfileDefineFromRun(&status, config, filerule); // File to return
+    pmFPAfile *file = pmFPAfileDefineFromRun(&status, bind, config, filerule); // File to return
     if (!status) {
         psError(PS_ERR_UNKNOWN, false, "Failed to load file definition for %s", filerule);
@@ -66,4 +66,5 @@
 static pmFPAfile *defineOutputFile(pmConfig *config, // Configuration
                                    pmFPAfile *template,    // File to use as basis for definition
+                                   bool source, // Is template a source (T), or for binding (F)?
                                    char *filerule,     // Name of file rule
                                    pmFPAfileType fileType // Type of file
@@ -71,5 +72,6 @@
 {
 
-    pmFPAfile *file = pmFPAfileDefineFromFile(config, template, 1, 1, filerule);
+    pmFPAfile *file = source ? pmFPAfileDefineFromFile(config, template, 1, 1, filerule) :
+        pmFPAfileDefineOutput(config, template->fpa, filerule);
     if (!file) {
         psError(PS_ERR_IO, false, _("Unable to generate output file from %s"), filerule);
@@ -96,5 +98,5 @@
 
     // look for the file on the RUN metadata
-    pmFPAfile *file = pmFPAfileDefineFromRun(&status, config, filerule); // File to return
+    pmFPAfile *file = pmFPAfileDefineFromRun(&status, bind, config, filerule); // File to return
     if (!status) {
         psError(PS_ERR_UNKNOWN, false, "Failed to load file definition for %s", filerule);
@@ -108,5 +110,5 @@
     // define new version of file
     if (!file) {
-        pmFPAfileDefineOutput(config, bind ? bind->fpa : NULL, filerule);
+        file = pmFPAfileDefineOutput(config, bind ? bind->fpa : NULL, filerule);
         if (!status) {
             psError(PS_ERR_UNKNOWN, false, "Failed to load file definition for %s", filerule);
@@ -160,6 +162,6 @@
 
     // Output image
-    pmFPAfile *output = defineOutputFile(config, input, "PPSUB.OUTPUT", PM_FPA_FILE_IMAGE);
-    pmFPAfile *outMask = defineOutputFile(config, output, "PPSUB.OUTPUT.MASK", PM_FPA_FILE_MASK);
+    pmFPAfile *output = defineOutputFile(config, input, true, "PPSUB.OUTPUT", PM_FPA_FILE_IMAGE);
+    pmFPAfile *outMask = defineOutputFile(config, output, false, "PPSUB.OUTPUT.MASK", PM_FPA_FILE_MASK);
     if (!output || !outMask) {
         psError(PS_ERR_UNKNOWN, false, "Unable to define output files");
@@ -170,5 +172,5 @@
     pmFPAfile *outVar = NULL;
     if (inVar && refVar) {
-        outVar = defineOutputFile(config, output, "PPSUB.OUTPUT.VARIANCE", PM_FPA_FILE_VARIANCE);
+        outVar = defineOutputFile(config, output, false, "PPSUB.OUTPUT.VARIANCE", PM_FPA_FILE_VARIANCE);
         if (!outVar) {
             psError(PS_ERR_UNKNOWN, false, "Unable to define output files");
@@ -180,6 +182,7 @@
 
     // Convolved input image
-    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);
+    pmFPAfile *inConvImage = defineOutputFile(config, input, true, "PPSUB.INPUT.CONV", PM_FPA_FILE_IMAGE);
+    pmFPAfile *inConvMask = defineOutputFile(config, inConvImage, false, "PPSUB.INPUT.CONV.MASK",
+                                             PM_FPA_FILE_MASK);
     if (!inConvImage || !inConvMask) {
         psError(PS_ERR_UNKNOWN, false, "Unable to define output files");
@@ -187,5 +190,5 @@
     }
     if (inVar) {
-        pmFPAfile *inConvVar = defineOutputFile(config, input, "PPSUB.INPUT.CONV.VARIANCE",
+        pmFPAfile *inConvVar = defineOutputFile(config, inConvImage, false, "PPSUB.INPUT.CONV.VARIANCE",
                                                 PM_FPA_FILE_VARIANCE);
         if (!inConvVar) {
@@ -196,6 +199,7 @@
 
     // Convolved ref image
-    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);
+    pmFPAfile *refConvImage = defineOutputFile(config, input, true, "PPSUB.REF.CONV", PM_FPA_FILE_IMAGE);
+    pmFPAfile *refConvMask = defineOutputFile(config, refConvImage, false, "PPSUB.REF.CONV.MASK",
+                                              PM_FPA_FILE_MASK);
     if (!refConvImage || !refConvMask) {
         psError(PS_ERR_UNKNOWN, false, "Unable to define output files");
@@ -203,5 +207,5 @@
     }
     if (refVar) {
-        pmFPAfile *refConvVar = defineOutputFile(config, input, "PPSUB.REF.CONV.VARIANCE",
+        pmFPAfile *refConvVar = defineOutputFile(config, refConvImage, false, "PPSUB.REF.CONV.VARIANCE",
                                                  PM_FPA_FILE_VARIANCE);
         if (!refConvVar) {
Index: /branches/cnb_branches/cnb_branch_20090301/ppSub/src/ppSubDefineOutput.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppSub/src/ppSubDefineOutput.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ppSub/src/ppSubDefineOutput.c	(revision 23594)
@@ -26,11 +26,15 @@
     psAssert(view, "Require view");
 
-    // generate an output readout
     pmCell *outCell = pmFPAfileThisCell(config->files, view, "PPSUB.OUTPUT"); // Output cell
-    pmReadout *outRO = pmReadoutAlloc(outCell); // Output readout: subtraction
     pmFPA *outFPA = outCell->parent->parent; // Output FPA
     pmHDU *outHDU = outFPA->hdu; // Output HDU
     if (!outHDU->header) {
         outHDU->header = psMetadataAlloc();
+    }
+
+    // generate an output readout (first check if it's already there by virtue of kernels)
+    pmReadout *outRO = pmFPAfileThisReadout(config->files, view, "PPSUB.OUTPUT.KERNELS"); // RO with kernel
+    if (!outRO) {
+        outRO = pmReadoutAlloc(outCell); // Output readout: subtraction
     }
 
Index: /branches/cnb_branches/cnb_branch_20090301/ppSub/src/ppSubMatchPSFs.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppSub/src/ppSubMatchPSFs.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ppSub/src/ppSubMatchPSFs.c	(revision 23594)
@@ -50,5 +50,5 @@
 
     // Load pre-calculated kernel, if available
-    pmReadout *kernelRO = pmFPAfileThisReadout(config->files, view, "PPSUB.OUTPUT.KERNEL"); // RO with kernel
+    pmReadout *kernelRO = pmFPAfileThisReadout(config->files, view, "PPSUB.OUTPUT.KERNELS"); // RO with kernel
 
     // Sources in image, used for stamps: these must be loaded from previous analysis stages
@@ -123,7 +123,6 @@
     float optThresh = psMetadataLookupF32(&mdok, recipe, "OPTIMUM.TOL"); // Tolerance for search
 
-    // XXX Do we need/want to define different values for BAD and POOR subtraction vs BAD and POOR warp?
     psImageMaskType maskVal = pmConfigMaskGet("MASK.VALUE", config); // Bits to mask in inputs
-    psImageMaskType maskPoor = pmConfigMaskGet("POOR.WARP", config); // Bits to mask for poor pixels
+    psImageMaskType maskPoor = pmConfigMaskGet("CONV.POOR", config); // Bits to mask for poor pixels
     psImageMaskType maskBad = pmConfigMaskGet("BLANK", config); // Bits to mask for bad pixels
 
Index: /branches/cnb_branches/cnb_branch_20090301/ppSub/src/ppSubSetMasks.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/ppSub/src/ppSubSetMasks.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/ppSub/src/ppSubSetMasks.c	(revision 23594)
@@ -43,6 +43,10 @@
     psAssert(satValue, "SAT must be non-zero");
 
-    psImageMaskType badValue = pmConfigMaskGet("BAD", config);
-    psAssert(badValue, "BAD must be non-zero");
+    psImageMaskType lowValue = pmConfigMaskGet("LOW", config);
+    if (!lowValue) {
+        // Look up old name for backward compatability
+        lowValue = pmConfigMaskGet("BAD", config);
+    }
+    psAssert(lowValue, "LOW or BAD must be non-zero");
 
     // input images
@@ -58,5 +62,5 @@
     if (!inRO->mask) {
         if (psMetadataLookupBool(NULL, recipe, "MASK.GENERATE")) {
-            pmReadoutSetMask(inRO, satValue, badValue);
+            pmReadoutSetMask(inRO, satValue, lowValue);
         } else {
             inRO->mask = psImageAlloc(numCols, numRows, PS_TYPE_IMAGE_MASK);
@@ -66,5 +70,5 @@
     if (!refRO->mask) {
         if (psMetadataLookupBool(NULL, recipe, "MASK.GENERATE")) {
-            pmReadoutSetMask(refRO, satValue, badValue);
+            pmReadoutSetMask(refRO, satValue, lowValue);
         } else {
             refRO->mask = psImageAlloc(numCols, numRows, PS_TYPE_IMAGE_MASK);
@@ -95,5 +99,5 @@
     float poorFrac = psMetadataLookupF32(&mdok, recipe, "POOR.FRACTION"); // Fraction for "poor"
 
-    psImageMaskType maskPoor = pmConfigMaskGet("POOR.WARP", config); // Bits to mask for poor pixels
+    psImageMaskType maskPoor = pmConfigMaskGet("CONV.POOR", config); // Bits to mask for poor pixels
     psImageMaskType maskBad = pmConfigMaskGet("BLANK", config); // Bits to mask for bad pixels
 
Index: /branches/cnb_branches/cnb_branch_20090301/psLib/src/db/psDB.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psLib/src/db/psDB.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/psLib/src/db/psDB.c	(revision 23594)
@@ -1447,5 +1447,5 @@
 
     mysqlType       *mType;             // type tmp variable
-    static bool     isNull = true;      // used in a MYSQL_BIND to indicate NULL
+    static my_bool isNull = true;       // used in a MYSQL_BIND to indicate NULL
 
     MYSQL_BIND *bind = mysqlRow->bind;
@@ -1472,7 +1472,5 @@
                 bind[i].length  = 0;
                 bind[i].buffer  = &item->data.U8;
-                bind[i].is_null = psDBIsPTypeNaN(item->type, &item->data.U8)
-                                  ? (my_bool *)&isNull
-                                  : NULL;
+                bind[i].is_null = psDBIsPTypeNaN(item->type, &item->data.U8) ? &isNull : NULL;
                 break;
             }
@@ -1480,7 +1478,5 @@
                 bind[i].length  = 0;
                 bind[i].buffer  = &item->data.U16;
-                bind[i].is_null = psDBIsPTypeNaN(item->type, &item->data.U16)
-                                  ? (my_bool *)&isNull
-                                  : NULL;
+                bind[i].is_null = psDBIsPTypeNaN(item->type, &item->data.U16) ? &isNull : NULL;
                 break;
             }
@@ -1488,7 +1484,5 @@
                 bind[i].length  = 0;
                 bind[i].buffer  = &item->data.U32;
-                bind[i].is_null = psDBIsPTypeNaN(item->type, &item->data.U32)
-                                  ? (my_bool *)&isNull
-                                  : NULL;
+                bind[i].is_null = psDBIsPTypeNaN(item->type, &item->data.U32) ? &isNull : NULL;
                 break;
             }
@@ -1496,7 +1490,5 @@
                 bind[i].length  = 0;
                 bind[i].buffer  = &item->data.U64;
-                bind[i].is_null = psDBIsPTypeNaN(item->type, &item->data.U64)
-                                  ? (my_bool *)&isNull
-                                  : NULL;
+                bind[i].is_null = psDBIsPTypeNaN(item->type, &item->data.U64) ? &isNull : NULL;
                 break;
             }
@@ -1504,7 +1496,5 @@
                 bind[i].length  = 0;
                 bind[i].buffer  = &item->data.S8;
-                bind[i].is_null = psDBIsPTypeNaN(item->type, &item->data.S8)
-                                  ? (my_bool *)&isNull
-                                  : NULL;
+                bind[i].is_null = psDBIsPTypeNaN(item->type, &item->data.S8) ? &isNull : NULL;
                 break;
             }
@@ -1512,7 +1502,5 @@
                 bind[i].length  = 0;
                 bind[i].buffer  = &item->data.S16;
-                bind[i].is_null = psDBIsPTypeNaN(item->type, &item->data.S16)
-                                  ? (my_bool *)&isNull
-                                  : NULL;
+                bind[i].is_null = psDBIsPTypeNaN(item->type, &item->data.S16) ? &isNull : NULL;
                 break;
             }
@@ -1520,7 +1508,5 @@
                 bind[i].length  = 0;
                 bind[i].buffer  = &item->data.S32;
-                bind[i].is_null = psDBIsPTypeNaN(item->type, &item->data.S32)
-                                  ? (my_bool *)&isNull
-                                  : NULL;
+                bind[i].is_null = psDBIsPTypeNaN(item->type, &item->data.S32) ? &isNull : NULL;
                 break;
             }
@@ -1528,7 +1514,5 @@
                 bind[i].length  = 0;
                 bind[i].buffer  = &item->data.S64;
-                bind[i].is_null = psDBIsPTypeNaN(item->type, &item->data.S64)
-                                  ? (my_bool *)&isNull
-                                  : NULL;
+                bind[i].is_null = psDBIsPTypeNaN(item->type, &item->data.S64) ? &isNull : NULL;
                 break;
             }
@@ -1536,7 +1520,5 @@
                 bind[i].length  = 0;
                 bind[i].buffer  = &item->data.F32;
-                bind[i].is_null = psDBIsPTypeNaN(item->type, &item->data.F32)
-                                  ? (my_bool *)&isNull
-                                  : NULL;
+                bind[i].is_null = psDBIsPTypeNaN(item->type, &item->data.F32) ? &isNull : NULL;
                 break;
             }
@@ -1544,7 +1526,5 @@
                 bind[i].length  = 0;
                 bind[i].buffer  = &item->data.F64;
-                bind[i].is_null = psDBIsPTypeNaN(item->type, &item->data.F64)
-                                  ? (my_bool *)&isNull
-                                  : NULL;
+                bind[i].is_null = psDBIsPTypeNaN(item->type, &item->data.F64) ? &isNull : NULL;
                 break;
             }
@@ -1558,7 +1538,5 @@
                 bind[i].length  = 0;
                 bind[i].buffer  = &item->data.B;
-                bind[i].is_null = psDBIsPTypeNaN(item->type, &item->data.B)
-                                  ? (my_bool *)&isNull
-                                  : NULL;
+                bind[i].is_null = psDBIsPTypeNaN(item->type, &item->data.B) ? &isNull : NULL;
                 break;
             }
@@ -1571,7 +1549,5 @@
                     bind[i].length  = &bind[i].buffer_length;
                     bind[i].buffer  = psStringCopy(item->data.V);
-                    bind[i].is_null = *item->data.str == '\0'
-                                      ? (my_bool *)&isNull
-                                      : NULL;
+                    bind[i].is_null = *item->data.str == '\0' ? &isNull : NULL;
                 } else {
                     // handles the case of NULL as a NULL database value
@@ -1579,5 +1555,5 @@
                     bind[i].length  = &bind[i].buffer_length;
                     bind[i].buffer  = NULL;
-                    bind[i].is_null = (my_bool *)&isNull;
+                    bind[i].is_null = &isNull;
                 }
                 break;
@@ -1625,5 +1601,5 @@
                     bind[i].length  = &bind[i].buffer_length;
                     bind[i].buffer  = NULL;
-                    bind[i].is_null = (my_bool *)&isNull;
+                    bind[i].is_null = &isNull;
                 }
                 break;
Index: /branches/cnb_branches/cnb_branch_20090301/psLib/src/fits/psFitsHeader.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psLib/src/fits/psFitsHeader.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/psLib/src/fits/psFitsHeader.c	(revision 23594)
@@ -569,4 +569,7 @@
                     continue;
                 }
+            } else if (keywordInList(name, noWriteCompressedKeys) ||
+                       (keyStarts && keywordStartsWith(name, noWriteCompressedKeyStarts))) {
+                continue;
             }
 
Index: /branches/cnb_branches/cnb_branch_20090301/psLib/src/imageops/psImageBinning.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psLib/src/imageops/psImageBinning.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/psLib/src/imageops/psImageBinning.c	(revision 23594)
@@ -31,5 +31,5 @@
 }
 
-psImageBinning *psImageBinningAlloc() {
+psImageBinning *psImageBinningAlloc(void) {
     psImageBinning *binning = (psImageBinning*)psAlloc(sizeof(psImageBinning));
     psMemSetDeallocator(binning, (psFreeFunc)psImageBinningFree);
Index: /branches/cnb_branches/cnb_branch_20090301/psLib/src/imageops/psImageBinning.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psLib/src/imageops/psImageBinning.h	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/psLib/src/imageops/psImageBinning.h	(revision 23594)
@@ -42,5 +42,5 @@
 
 
-psImageBinning *psImageBinningAlloc() PS_ATTR_MALLOC;
+psImageBinning *psImageBinningAlloc(void) PS_ATTR_MALLOC;
 bool psMemCheckBinning(psPtr ptr);
 
Index: /branches/cnb_branches/cnb_branch_20090301/psLib/src/imageops/psImageCovariance.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psLib/src/imageops/psImageCovariance.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/psLib/src/imageops/psImageCovariance.c	(revision 23594)
@@ -260,4 +260,8 @@
 {
     PS_ASSERT_IMAGE_NON_NULL(variance, NULL);
+    if (!covar) {
+        // Transferred all we could!
+        return true;
+    }
     PS_ASSERT_KERNEL_NON_NULL(covar, NULL);
 
Index: /branches/cnb_branches/cnb_branch_20090301/psLib/src/jpeg/psImageJpeg.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psLib/src/jpeg/psImageJpeg.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/psLib/src/jpeg/psImageJpeg.c	(revision 23594)
@@ -35,5 +35,5 @@
 }
 
-psImageJpegColormap *psImageJpegColormapAlloc ()
+psImageJpegColormap *psImageJpegColormapAlloc(void)
 {
     psImageJpegColormap *map = psAlloc(sizeof(psImageJpegColormap));
Index: /branches/cnb_branches/cnb_branch_20090301/psLib/src/jpeg/psImageJpeg.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psLib/src/jpeg/psImageJpeg.h	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/psLib/src/jpeg/psImageJpeg.h	(revision 23594)
@@ -1,5 +1,5 @@
 /* @file  psImageJpeg.h
  * @brief functions to generate JPEG images from psImage
- * 
+ *
  * @author EAM
  * @version $Revision: 1.3 $ $Name: not supported by cvs2svn $
@@ -28,5 +28,5 @@
 
 // allocate a colormap (does not define the map values)
-psImageJpegColormap *psImageJpegColormapAlloc() PS_ATTR_MALLOC;
+psImageJpegColormap *psImageJpegColormapAlloc(void) PS_ATTR_MALLOC;
 
 // set the colormap values using the supplied name
Index: /branches/cnb_branches/cnb_branch_20090301/psLib/src/math/psMinimizeLMM.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psLib/src/math/psMinimizeLMM.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/psLib/src/math/psMinimizeLMM.c	(revision 23594)
@@ -95,8 +95,8 @@
     // check for non-finite entries in result
     for (int i = 0; i < Beta->n; i++) {
-	if (!isfinite(Beta->data.F32[i])) {
-	    // psError(PS_ERR_BAD_PARAMETER_VALUE, 3, "Fit value diverges: vector[%d] is %.2f\n", i, Beta->data.F32[i]);
-	    return false;
-	}
+        if (!isfinite(Beta->data.F32[i])) {
+            // psError(PS_ERR_BAD_PARAMETER_VALUE, 3, "Fit value diverges: vector[%d] is %.2f\n", i, Beta->data.F32[i]);
+            return false;
+        }
     }
 
@@ -104,8 +104,8 @@
     // (we must do this before truncating Beta below)
     if (dLinear) {
-	*dLinear = psMinLM_dLinear(Beta, beta, lambda);
-    }
-
-    // full-length Beta for checkLimits functions 
+        *dLinear = psMinLM_dLinear(Beta, beta, lambda);
+    }
+
+    // full-length Beta for checkLimits functions
     psVector *tmpBeta = psVectorAlloc(params->n, PS_TYPE_F32);
     psVectorInit (tmpBeta, 0.0);
@@ -113,7 +113,7 @@
     // set tmpBeta values which are not masked
     for (int j = 0, n = 0; j < params->n; j++) {
-	if (paramMask && (paramMask->data.PS_TYPE_VECTOR_MASK_DATA[j])) continue;
-	tmpBeta->data.F32[j] = Beta->data.F32[n];
-	n++;
+        if (paramMask && (paramMask->data.PS_TYPE_VECTOR_MASK_DATA[j])) continue;
+        tmpBeta->data.F32[j] = Beta->data.F32[n];
+        n++;
     }
 
@@ -127,5 +127,5 @@
         if (checkLimits) {
             checkLimits (PS_MINIMIZE_BETA_LIMIT, j, Params->data.F32, tmpBeta->data.F32);
-	}
+        }
 
         Params->data.F32[j] = params->data.F32[j] - tmpBeta->data.F32[j];
@@ -135,12 +135,12 @@
             checkLimits (PS_MINIMIZE_PARAM_MIN,  j, Params->data.F32, tmpBeta->data.F32);
             checkLimits (PS_MINIMIZE_PARAM_MAX,  j, Params->data.F32, tmpBeta->data.F32);
-	}
+        }
     }
 
     // apply tmpBeta after limits have been checked
     for (int j = 0, n = 0; j < params->n; j++) {
-	if (paramMask && (paramMask->data.PS_TYPE_VECTOR_MASK_DATA[j])) continue;
-	Beta->data.F32[n] = tmpBeta->data.F32[j];
-	n++;
+        if (paramMask && (paramMask->data.PS_TYPE_VECTOR_MASK_DATA[j])) continue;
+        Beta->data.F32[n] = tmpBeta->data.F32[j];
+        n++;
     }
 
@@ -169,5 +169,5 @@
     // if none are available, return false
     if (!psMinLM_AllocAB (&Alpha, &Beta, params, paramMask)) {
-	return false;
+        return false;
     }
 
@@ -241,7 +241,7 @@
     // beta only counts unmasked parameters
     for (int i = 0; i < beta->n; i++) {
-	dh = lambda*B[i] + b[i];
-	sh = 0.5*B[i]*dh;
-	Sh += sh;
+        dh = lambda*B[i] + b[i];
+        sh = 0.5*B[i]*dh;
+        Sh += sh;
         dLinear += lambda*PS_SQR(B[i]) + B[i]*b[i];
     }
@@ -295,5 +295,5 @@
         assert (!isnan(chisq));
 
-	// we track alpha,beta and params,deriv separately
+        // we track alpha,beta and params,deriv separately
         for (int j = 0, J = 0; j < params->n; j++) {
             if (paramMask && (paramMask->data.PS_TYPE_VECTOR_MASK_DATA[j])) continue;
@@ -304,8 +304,8 @@
                 if (paramMask && (paramMask->data.PS_TYPE_VECTOR_MASK_DATA[k])) continue;
                 alpha->data.F32[J][K] += weight * deriv->data.F32[k];
-		K++;
+                K++;
             }
             beta->data.F32[J] += weight * delta;
-	    J++;
+            J++;
         }
     }
@@ -328,5 +328,5 @@
 between that function at the specified coords and the specified value at those
 coords.
- 
+
 This requires F32 input data; all internal calls use F32.
 XXX Make an F64 version?
@@ -386,7 +386,7 @@
     // Alpha & Beta only contain elements to represent the unmasked parameters
     if (!psMinLM_AllocAB (&Alpha, &Beta, params, paramMask)) {
-	psAbort ("programming error: no unmasked parameters to be fit\n");
-    }
-    
+        psAbort ("programming error: no unmasked parameters to be fit\n");
+    }
+
     psImage *alpha   = psImageAlloc(Alpha->numCols, Alpha->numRows, PS_TYPE_F32);
     psVector *beta   = psVectorAlloc(Beta->n, PS_TYPE_F32);
@@ -490,18 +490,18 @@
             psTrace ("psLib.math", 5, "failure to calculate covariance matrix\n");
         }
-	// set covar values which are not masked
-	psImageInit (covar, 0.0);
-	for (int j = 0, J = 0; j < params->n; j++) {
-	    if (paramMask && (paramMask->data.PS_TYPE_VECTOR_MASK_DATA[j])) {
-		covar->data.F32[j][j] = 1.0;
-		continue;
-	    }
-	    for (int k = 0, K = 0; k < params->n; k++) {
-		if (paramMask && (paramMask->data.PS_TYPE_VECTOR_MASK_DATA[k])) continue;
-		covar->data.F32[j][k] = Alpha->data.F32[J][K];
-		K++;
-	    }
-	    J++;
-	}
+        // set covar values which are not masked
+        psImageInit (covar, 0.0);
+        for (int j = 0, J = 0; j < params->n; j++) {
+            if (paramMask && (paramMask->data.PS_TYPE_VECTOR_MASK_DATA[j])) {
+                covar->data.F32[j][j] = 1.0;
+                continue;
+            }
+            for (int k = 0, K = 0; k < params->n; k++) {
+                if (paramMask && (paramMask->data.PS_TYPE_VECTOR_MASK_DATA[k])) continue;
+                covar->data.F32[j][k] = Alpha->data.F32[J][K];
+                K++;
+            }
+            J++;
+        }
     }
 
@@ -533,13 +533,13 @@
     // count unmasked parameters
     if (paramMask) {
-	nParams = 0;
-	for (int i = 0; i < paramMask->n; i++) {
-	    if (paramMask->data.PS_TYPE_VECTOR_MASK_DATA[i]) continue;
-	    nParams ++;
-	}
-    }
-
-    if (nParams == 0) { 
-	return false;
+        nParams = 0;
+        for (int i = 0; i < paramMask->n; i++) {
+            if (paramMask->data.PS_TYPE_VECTOR_MASK_DATA[i]) continue;
+            nParams ++;
+        }
+    }
+
+    if (nParams == 0) {
+        return false;
     }
 
@@ -585,5 +585,5 @@
 }
 
-psMinConstraint* psMinConstraintAlloc()
+psMinConstraint* psMinConstraintAlloc(void)
 {
     psMinConstraint *tmp = psAlloc(sizeof(psMinConstraint));
Index: /branches/cnb_branches/cnb_branch_20090301/psLib/src/math/psMinimizeLMM.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psLib/src/math/psMinimizeLMM.h	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/psLib/src/math/psMinimizeLMM.h	(revision 23594)
@@ -94,5 +94,5 @@
 psMinConstraint;
 
-psMinConstraint *psMinConstraintAlloc() PS_ATTR_MALLOC;
+psMinConstraint *psMinConstraintAlloc(void) PS_ATTR_MALLOC;
 
 /** Allocates a psMinimization structure.
@@ -174,5 +174,5 @@
     psF32 lambda);
 
-// allocate alpha and beta for unmasked parameters only 
+// allocate alpha and beta for unmasked parameters only
 bool psMinLM_AllocAB (psImage **Alpha, psVector **Beta, const psVector *params, const psVector *paramMask);
 
Index: /branches/cnb_branches/cnb_branch_20090301/psLib/src/math/psSparse.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psLib/src/math/psSparse.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/psLib/src/math/psSparse.c	(revision 23594)
@@ -245,4 +245,7 @@
 psSparseBorder *psSparseBorderAlloc(psSparse *sparse, int Nborder)
 {
+    psAssert(sparse->Nrows > 0, "Require positive size: nrows = %d", sparse->Nrows);
+    psAssert(Nborder > 0, "Require positive size: nborder = %d", Nborder);
+
     psSparseBorder *border = (psSparseBorder *)psAlloc(sizeof(psSparseBorder));
     psMemSetDeallocator(border, (psFreeFunc) psSparseBorderFree);
@@ -451,12 +454,12 @@
         square = psImageCopy (square, border->Tjj, PS_TYPE_F64);
         if (!psMatrixGJSolve (square, Go)) {
-	    psError(PS_ERR_UNKNOWN, false, "Unable to solve for lower square.");
-	    psFree (dG);
-	    psFree (Go);
-	    psFree (dF);
-	    psFree (Fo);
-	    psFree (square);
-	    return false;
-	}
+            psError(PS_ERR_UNKNOWN, false, "Unable to solve for lower square.");
+            psFree (dG);
+            psFree (Go);
+            psFree (dF);
+            psFree (Fo);
+            psFree (square);
+            return false;
+        }
         yVec = psVectorCopy (yVec, Go, PS_TYPE_F32);
 
Index: /branches/cnb_branches/cnb_branch_20090301/psLib/src/math/psSpline.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psLib/src/math/psSpline.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/psLib/src/math/psSpline.c	(revision 23594)
@@ -103,16 +103,16 @@
 tabulated function at n points, this routine calculates the second
 derivatives of the interpolating cubic splines at those n points.
- 
+
 The first and second derivatives at the endpoints were previously undefined in
 the SDR.  From bugzilla #???, they are required to be 0.0, implementing natural
 splines.
- 
+
 Endpoints are defined by
     PS_LEFT_SPLINE_DERIV
     PS_RIGHT_SPLINE_DERIV
- 
+
 This routine assumes that vectors x and y are of the appropriate types/sizes
 (F32).
- 
+
 XXX: use recycled vectors for internal data.
 XXX: do an F64 version?
@@ -177,5 +177,5 @@
 }
 
-psSpline1D *psSpline1DAlloc()
+psSpline1D *psSpline1DAlloc(void)
 {
     psSpline1D *tmpSpline = (psSpline1D *) psAlloc(sizeof(psSpline1D));
@@ -192,5 +192,5 @@
 psVectorFitSpline1D(): given a set of x/y vectors, this routine generates the
 linear or cublic splines which satisfy those data points.
- 
+
 The formula for calculating the spline polynomials is derived from Numerical
 Recipes in C.  The basic idea is that the polynomial is
@@ -206,5 +206,5 @@
 into a polynomial in terms of x, and then saving the coefficients of the
 powers of x in the spline polynomials.  This gets pretty complicated.
- 
+
 XXX: What types must be supported?
  *****************************************************************************/
@@ -354,8 +354,8 @@
 (vectorBinDisectF32()).  Then it evaluates the spline at that x location
 by a call to the 1D polynomial functions.
- 
+
 XXX: The spline eval functions require input and output to be F32.  however
      the spline fit functions require F32 and F64.
- 
+
 XXX: This only works if spline->knots if psF32.  Must we add support for psU32 and
 psF64?
Index: /branches/cnb_branches/cnb_branch_20090301/psLib/src/math/psSpline.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psLib/src/math/psSpline.h	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/psLib/src/math/psSpline.h	(revision 23594)
@@ -46,5 +46,5 @@
  *  @return psSpline1D*    new 1-D spline struct
  */
-psSpline1D *psSpline1DAlloc() PS_ATTR_MALLOC;
+psSpline1D *psSpline1DAlloc(void) PS_ATTR_MALLOC;
 
 /** Evaluates 1-D spline polynomials at a specific coordinate.
Index: /branches/cnb_branches/cnb_branch_20090301/psLib/src/math/psStats.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psLib/src/math/psStats.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/psLib/src/math/psStats.c	(revision 23594)
@@ -265,17 +265,12 @@
     for (long i = 0; i < num; i++) {
         // Check if the data is with the specified range
-        if (maskData && (maskData[i] & maskVal)) {
+	if (!isfinite(vector[i])) 
+	    continue;
+        if (useRange && (vector[i] < stats->min))
             continue;
-        }
-
-        if (useRange) {
-            if (vector[i] < stats->min || vector[i] > stats->max) {
-                continue;
-            }
-        } else if (!isfinite(vector[i])) {
-            stats->max = stats->min = NAN;
-            psError(PS_ERR_IEEE, true, "Element %ld of vector is Inf/NaN", i);
-            return 0;
-        }
+        if (useRange && (vector[i] > stats->max))
+            continue;
+        if (maskData && (maskData[i] & maskVal))
+            continue;
 
         numValid++;
Index: /branches/cnb_branches/cnb_branch_20090301/psLib/src/mathtypes/psImage.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psLib/src/mathtypes/psImage.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/psLib/src/mathtypes/psImage.c	(revision 23594)
@@ -55,8 +55,8 @@
 
         // drop my entry on my parent's array of children
-	// lock parent before freeing child : psArrayRemoveDataNoFree is NOT thread safe
-	psMutexLock (parent);
+        // lock parent before freeing child : psArrayRemoveDataNoFree is NOT thread safe
+        psMutexLock (parent);
         psArrayRemoveDataNoFree (parent->children, image);
-	psMutexUnlock (parent);
+        psMutexUnlock (parent);
 
         // drop my reference to my parent
@@ -87,15 +87,6 @@
     int rowSize = numCols * elementSize;        // row size in bytes.
 
-    if (numRows < 1) {
-        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
-                _("Specified number of rows (%d) or columns (%d) is invalid."),
-                numRows, numCols);
-        return NULL;
-    }
-    if (numCols < 1) {
-        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
-                _("Specified number of rows (%d) or columns (%d) is invalid."),
-                numRows, numCols);
-        return NULL;
+    if (numRows < 1 || numCols < 1) {
+        psAbort(_("Specified number of rows (%d) or columns (%d) is invalid."), numRows, numCols);
     }
 
Index: /branches/cnb_branches/cnb_branch_20090301/psLib/src/sys/psError.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psLib/src/sys/psError.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/psLib/src/sys/psError.c	(revision 23594)
@@ -146,10 +146,10 @@
 
 psErrorCode p_psErrorV(const char* filename,
-		       unsigned int lineno,
-		       const char* func,
-		       psErrorCode code,
-		       bool new,
-		       const char* format,
-		       va_list ap)
+                       unsigned int lineno,
+                       const char* func,
+                       psErrorCode code,
+                       bool new,
+                       const char* format,
+                       va_list ap)
 {
   char errMsg[MAX_STRING_LENGTH];
@@ -245,5 +245,5 @@
 }
 
-long psErrorGetStackSize()
+long psErrorGetStackSize(void)
 {
     psArray *errorStack = psErrorStackGet();
Index: /branches/cnb_branches/cnb_branch_20090301/psLib/src/sys/psError.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psLib/src/sys/psError.h	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/psLib/src/sys/psError.h	(revision 23594)
@@ -87,5 +87,5 @@
  *  @return int The number of items on the error stack
  */
-long psErrorGetStackSize();
+long psErrorGetStackSize(void);
 
 
Index: /branches/cnb_branches/cnb_branch_20090301/psLib/src/sys/psLogMsg.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psLib/src/sys/psLogMsg.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/psLib/src/sys/psLogMsg.c	(revision 23594)
@@ -76,5 +76,5 @@
 }
 
-int psLogGetLevel()
+int psLogGetLevel(void)
 {
     return globalLogLevel;
Index: /branches/cnb_branches/cnb_branch_20090301/psLib/src/sys/psLogMsg.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psLib/src/sys/psLogMsg.h	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/psLib/src/sys/psLogMsg.h	(revision 23594)
@@ -63,5 +63,5 @@
  *  @return int:        The current file descriptor.
  */
-int psLogGetDestination();
+int psLogGetDestination(void);
 
 
@@ -83,5 +83,5 @@
  *  @return int:        The current logging level.
  */
-int psLogGetLevel();
+int psLogGetLevel(void);
 
 /** This procedure sets the log format for future log messages.  The argument
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/astrom/pmAstrometryDistortion.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/astrom/pmAstrometryDistortion.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/astrom/pmAstrometryDistortion.c	(revision 23594)
@@ -40,5 +40,5 @@
 }
 
-pmAstromGradient *pmAstromGradientAlloc ()
+pmAstromGradient *pmAstromGradientAlloc (void)
 {
 
@@ -126,5 +126,5 @@
             grad = pmAstromGradientAlloc ();
 
-	    // XXX psTraceSetLevel("psLib.math.psVectorClipFitPolynomial2D", 7);
+            // XXX psTraceSetLevel("psLib.math.psVectorClipFitPolynomial2D", 7);
 
             // fit the collection of positions and offsets with a local 1st order gradient
@@ -138,5 +138,5 @@
             grad->dTPdM.x = local->coeff[0][1];
 
-	    // XXX psTraceSetLevel("psLib.math.psVectorClipFitPolynomial2D", 0);
+            // XXX psTraceSetLevel("psLib.math.psVectorClipFitPolynomial2D", 0);
 
             // fit the collection of positions and offsets with a local 1st order gradient
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/astrom/pmAstrometryDistortion.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/astrom/pmAstrometryDistortion.h	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/astrom/pmAstrometryDistortion.h	(revision 23594)
@@ -27,5 +27,5 @@
 pmAstromGradient;
 
-pmAstromGradient *pmAstromGradientAlloc ();
+pmAstromGradient *pmAstromGradientAlloc (void);
 
 /* The following function determines the position residual, in the tangent
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/astrom/pmAstrometryVisual.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/astrom/pmAstrometryVisual.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/astrom/pmAstrometryVisual.c	(revision 23594)
@@ -47,5 +47,5 @@
 /* Initialization Routines  */
 
-bool pmAstromVisualClose()
+bool pmAstromVisualClose(void)
 {
     if(kapa != -1)
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/astrom/pmAstrometryVisual.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/astrom/pmAstrometryVisual.h	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/astrom/pmAstrometryVisual.h	(revision 23594)
@@ -26,5 +26,5 @@
 /** Close plotting windows at the end of a run
  * @return true for success */
-bool pmAstromVisualClose();
+bool pmAstromVisualClose(void);
 
 
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/camera/pmFPARead.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/camera/pmFPARead.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/camera/pmFPARead.c	(revision 23594)
@@ -554,5 +554,9 @@
           }
           psFree(header);
-          bad = pmConfigMaskGet("BAD", config);
+          bad = pmConfigMaskGet("LOW", config);
+          if (!bad) {
+              // XXX look up old name for compatability
+              bad = pmConfigMaskGet("BAD", config);
+          }
           break;
       }
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/camera/pmFPAWrite.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/camera/pmFPAWrite.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/camera/pmFPAWrite.c	(revision 23594)
@@ -172,7 +172,5 @@
 
     if (writeBlank || writeImage) {
-        pmConceptSource source = PM_CONCEPT_SOURCE_HEADER | PM_CONCEPT_SOURCE_CELLS |
-                                 PM_CONCEPT_SOURCE_DEFAULTS | PM_CONCEPT_SOURCE_DATABASE;
-        if (!pmConceptsWriteCell(cell, source, true, config)) {
+        if (!pmConceptsWriteCell(cell, true, config)) {
             psError(PS_ERR_IO, false, "Unable to write concepts for cell.");
             return false;
@@ -225,7 +223,5 @@
 
         if (writeBlank || writeImage) {
-            pmConceptSource source = PM_CONCEPT_SOURCE_HEADER | PM_CONCEPT_SOURCE_CELLS |
-                                     PM_CONCEPT_SOURCE_DEFAULTS | PM_CONCEPT_SOURCE_DATABASE;
-            if (!pmConceptsWriteChip(chip, source, true, true, config)) {
+            if (!pmConceptsWriteChip(chip, true, true, config)) {
                 psError(PS_ERR_IO, false, "Unable to write concepts for chip.\n");
                 return false;
@@ -294,7 +290,5 @@
 
         if (writeBlank || writeImage) {
-            pmConceptSource source = PM_CONCEPT_SOURCE_HEADER | PM_CONCEPT_SOURCE_CELLS |
-                                     PM_CONCEPT_SOURCE_DEFAULTS | PM_CONCEPT_SOURCE_DATABASE;
-            if (!pmConceptsWriteFPA(fpa, source, true, config)) {
+            if (!pmConceptsWriteFPA(fpa, true, config)) {
                 psError(PS_ERR_IO, false, "Unable to write concepts for FPA.\n");
                 return false;
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/camera/pmFPAfile.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/camera/pmFPAfile.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/camera/pmFPAfile.c	(revision 23594)
@@ -20,4 +20,6 @@
 #include "pmConceptsCopy.h"
 
+static int fileNum = 0;                 // Number of file
+
 static void pmFPAfileFree(pmFPAfile *file)
 {
@@ -56,5 +58,5 @@
 }
 
-pmFPAfile *pmFPAfileAlloc()
+pmFPAfile *pmFPAfileAlloc(void)
 {
     pmFPAfile *file = psAlloc(sizeof(pmFPAfile));
@@ -100,4 +102,6 @@
 
     file->save = false;
+
+    file->index = fileNum++;
 
     file->imageId = 0;
@@ -350,4 +354,12 @@
             psStringSubstitute(&newRule, name, "{OUTPUT}");
         }
+    }
+
+    if (strstr(newRule, "{FILE.INDEX}")) {
+        // Number of the file in list
+        psString num = NULL;            // Number to use
+        psStringAppend(&num, "%d", file->index);
+        psStringSubstitute(&newRule, num, "{FILE.INDEX}");
+        psFree(num);
     }
 
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/camera/pmFPAfile.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/camera/pmFPAfile.h	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/camera/pmFPAfile.h	(revision 23594)
@@ -109,9 +109,11 @@
     psString formatName;                // name of the camera format
 
+    int index;                          // Index of file
+
     psS64 imageId, sourceId;            // Image and source identifiers
 } pmFPAfile;
 
 // allocate an empty pmFPAfile structure
-pmFPAfile *pmFPAfileAlloc ();
+pmFPAfile *pmFPAfileAlloc(void);
 
 // select the readout from the named pmFPAfile; if the named file does not exist,
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/camera/pmFPAfileDefine.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/camera/pmFPAfileDefine.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/camera/pmFPAfileDefine.c	(revision 23594)
@@ -382,5 +382,6 @@
 
     // place the resulting file in the config system
-    psMetadataAddPtr(config->files, PS_LIST_TAIL, name, PS_DATA_UNKNOWN, "Output file", file);
+    psMetadataAddPtr(config->files, PS_LIST_TAIL, name, PS_DATA_UNKNOWN | PS_META_DUPLICATE_OK,
+                     "Output file", file);
     psFree(file);                       // we free this copy of file, but 'files' still has a copy
     return file;                        // the returned value is a view into the version on 'files'
@@ -719,5 +720,5 @@
 }
 
-pmFPAfile *pmFPAfileDefineFromRun(bool *success, pmConfig *config, const char *filename)
+pmFPAfile *pmFPAfileDefineFromRun(bool *success, pmFPAfile *bind, pmConfig *config, const char *filename)
 {
     PS_ASSERT_PTR_NON_NULL(config, NULL);
@@ -732,5 +733,5 @@
     }
 
-    pmFPAfile *file = fpaFileDefineFromArray(config, NULL, filename, filenames); // File of interest
+    pmFPAfile *file = fpaFileDefineFromArray(config, bind, filename, filenames); // File of interest
     psFree(filenames);
 
@@ -742,4 +743,49 @@
 }
 
+psArray *pmFPAfileDefineMultipleFromRun(bool *success, psArray *bind, pmConfig *config, const char *filename)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+    PS_ASSERT_STRING_NON_EMPTY(filename, NULL);
+
+    if (success) {
+        *success = false;
+    }
+
+    psArray *files = pmConfigRunFileGet(config, filename); // Filenames used, to return
+    if (!files || files->n == 0) {
+        if (success) {
+            *success = true;
+        }
+        return NULL;
+    }
+    if (bind && files->n != bind->n) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "Length of filenames (%ld) and bind files (%ld) does not match.",
+                files->n, bind->n);
+        psFree(files);
+        return NULL;
+    }
+
+    psArray *dummy = psArrayAlloc(1);   // Dummy array of single filename
+    for (int i = 0; i < files->n; i++) {
+        psFree(dummy->data[0]);
+        dummy->data[0] = files->data[i];
+        pmFPAfile *bindFile = bind ? bind->data[i] : NULL; // File to which to bind
+        files->data[i] = psMemIncrRefCounter(fpaFileDefineFromArray(config, bindFile, filename, dummy));
+        if (!files->data[i]) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to define file %s %d", filename, i);
+            psFree(dummy);
+            psFree(files);
+            return NULL;
+        }
+    }
+    psFree(dummy);
+
+    if (success) {
+        *success = true;
+    }
+
+    return files;
+}
 
 // define the named pmFPAfile from the camera->config
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/camera/pmFPAfileDefine.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/camera/pmFPAfileDefine.h	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/camera/pmFPAfileDefine.h	(revision 23594)
@@ -67,8 +67,18 @@
 pmFPAfile *pmFPAfileDefineFromRun(
     bool *found,                        ///< Found files?
+    pmFPAfile *bind,                    ///< File to which to bind, or NULL
     pmConfig *config,                   ///< Configuration
     const char *filename                ///< Name of file
     );
 
+/// Define multiple files based on the filenames in the RUN metadata in the configuration
+///
+/// An array of the files defined is returned
+psArray *pmFPAfileDefineMultipleFromRun(
+    bool *found,                        ///< Found files?
+    psArray *bind,                      ///< Files to which to bind, or NULL
+    pmConfig *config,                   ///< Configuration
+    const char *filename                ///< Name of file
+    );
 
 // look for the given argname on the argument list.  find the give filename from the file rules
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/camera/pmFPAfileFitsIO.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/camera/pmFPAfileFitsIO.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/camera/pmFPAfileFitsIO.c	(revision 23594)
@@ -183,17 +183,15 @@
           }
 
-          pmConceptSource sources = PM_CONCEPT_SOURCE_HEADER | PM_CONCEPT_SOURCE_CELLS |
-              PM_CONCEPT_SOURCE_DEFAULTS | PM_CONCEPT_SOURCE_DATABASE; // Concept sources to write
           if (cell) {
-              if (!pmConceptsWriteCell(cell, sources, true, config)) {
+              if (!pmConceptsWriteCell(cell, true, config)) {
                   psError(PS_ERR_IO, false, "Unable to write concepts for cell.\n");
                   return false;
               }
           } else if (chip) {
-              if (!pmConceptsWriteChip(chip, sources, true, true, config)) {
+              if (!pmConceptsWriteChip(chip, true, true, config)) {
                   psError(PS_ERR_IO, false, "Unable to write concepts for chip.\n");
                   return false;
               }
-          } else if (!pmConceptsWriteFPA(fpa, sources, true, config)) {
+          } else if (!pmConceptsWriteFPA(fpa, true, config)) {
               psError(PS_ERR_IO, false, "Unable to write concepts for FPA.\n");
               return false;
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/camera/pmFPAfileIO.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/camera/pmFPAfileIO.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/camera/pmFPAfileIO.c	(revision 23594)
@@ -166,5 +166,5 @@
     }
 
-    if (!pmConfigRunFileAdd(config, file)) {
+    if (!pmConfigRunFileAddRead(config, file)) {
         psError(PS_ERR_UNKNOWN, false, "Unable to add file to run-time information");
         return false;
@@ -421,5 +421,5 @@
     }
 
-    if (!pmConfigRunFileAdd(config, file)) {
+    if (!pmConfigRunFileAddWrite(config, file)) {
         psError(PS_ERR_UNKNOWN, false, "Unable to add file to run-time information");
         return false;
@@ -635,4 +635,74 @@
 }
 
+psString pmFPAfileName(const pmFPAfile *file, const pmFPAview *view, pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(file, NULL);
+    PS_ASSERT_PTR_NON_NULL(view, NULL);
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    psString filename = pmFPAfileNameFromRule(file->filerule, file, view); // Filename, based on rule
+    if (!filename) {
+        psError(PS_ERR_IO, true, "Cannot determine file name from rule");
+        return false;
+    }
+
+    // indirect filenames: these come from a list on the command line or elsewhere
+    if (!strcasecmp(filename, "@FILES")) {
+        psString filesrc = pmFPAfileNameFromRule(file->filesrc, file, view); // Source of file name
+        if (!filesrc) {
+            psError(PS_ERR_IO, false, "error converting filesrc to name %s", file->filesrc);
+            return false;
+        }
+        psFree(filename);
+        filename = psMemIncrRefCounter(psMetadataLookupStr(NULL, file->names, filesrc));
+        if (!filename) {
+            psError(PS_ERR_IO, false, "filename lookup error (@FILES) for %s : %s", file->filesrc, filesrc);
+            psFree(filesrc);
+            return false;
+        }
+        psFree(filesrc);
+    }
+
+    // get name from detrend database
+    // file->detrend->detID contains the desired -det_id detID -iteration iter string
+    if (!strcasecmp(filename, "@DETDB")) {
+        if (!file->detrend) {
+            psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find information about selected detrend.");
+            return false;
+        }
+        psMetadata *menu = psMetadataLookupMetadata(NULL, file->camera, "CLASSID"); // Menu of class IDs
+        if (!menu) {
+            psError(PS_ERR_IO, false, "Unable to find CLASSID metadata in camera configuration");
+            return false;
+        }
+        const char *rule = psMetadataLookupStr(NULL, menu, file->detrend->level); // Rule for class_id
+        if (!rule || strlen(rule) == 0) {
+            psError(PS_ERR_IO, false, "Unable to find %s in CLASSID in camera configuration",
+                    file->detrend->level);
+            return false;
+        }
+        psString classId = pmFPAfileNameFromRule(rule, file, view); // The class identifier, for pmDetrendFile
+        if (!classId) {
+            psError(PS_ERR_IO, false, "error converting CLASSID rule to name: %s\n", rule);
+            return false;
+        }
+
+        psTrace("psModules.camera", 6, "looking for detrend (%s, %s)\n", file->detrend->detID, classId);
+        psFree(filename);
+        filename = pmDetrendFile(file->detrend->detID, classId, config);
+        if (!filename) {
+            psError(PS_ERR_IO, false, "failed to find a valid detrend image for detID %s : classID %s",
+                    file->detrend->detID, classId);
+            psFree(classId);
+            return false;
+        }
+
+        psTrace("psModules.camera", 6, "got detrend file %s", filename);
+        psFree(classId);
+    }
+
+    return filename;
+}
+
 // open file (if not already opened).
 // this function is only called only within pmFPAfileRead or pmFPAfileWrite.
@@ -672,68 +742,9 @@
 
     // determine the file name, free a name allocated earlier
-    psFree (file->filename);
-    file->filename = pmFPAfileNameFromRule (file->filerule, file, view);
-    if (file->filename == NULL) {
-        psError(PS_ERR_IO, true, "Filename is NULL");
-        return false;
-    }
-
-    // indirect filenames: these come from a list on the command line or elsewhere
-    if (!strcasecmp (file->filename, "@FILES")) {
-        char *filesrc = pmFPAfileNameFromRule (file->filesrc, file, view);
-        if (filesrc == NULL) {
-            psError(PS_ERR_IO, false, "error converting filesrc to name %s\n", file->filesrc);
-            return false;
-        }
-
-        psFree (file->filename);
-        file->filename = psMetadataLookupStr (&status, file->names, filesrc);
-
-        if (file->filename == NULL) {
-            psError(PS_ERR_IO, true, "filename lookup error (@FILES) for %s : %s\n", file->filesrc, filesrc);
-            psFree (filesrc);
-            return false;
-        }
-        // psMetadataLookupStr just returns a view, file->filename must be protected
-        psMemIncrRefCounter (file->filename);
-        psFree (filesrc);
-    }
-
-    // get name from detrend database
-    // file->detrend->detID contains the desired -det_id detID -iteration iter string
-    if (!strcasecmp (file->filename, "@DETDB")) {
-        if (!file->detrend) {
-            psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find information about selected detrend.");
-            return false;
-        }
-
-        psString classId = NULL;        // The class identifier, to pass to pmDetrendFile
-        psMetadata *menu = psMetadataLookupMetadata(&status, file->camera, "CLASSID"); // Menu of class IDs
-        if (!status || !menu) {
-            psError(PS_ERR_IO, false, "Unable to find CLASSID metadata in camera configuration");
-            return false;
-        }
-        const char *rule = psMetadataLookupStr(&status, menu, file->detrend->level); // Rule for class_id
-        if (!status || !rule || strlen(rule) == 0) {
-            psError(PS_ERR_IO, false, "Unable to find %s in CLASSID in camera configuration", file->detrend->level);
-            return false;
-        }
-        classId = pmFPAfileNameFromRule(rule, file, view);
-        if (!classId) {
-            psError(PS_ERR_IO, false, "error converting CLASSID rule to name: %s\n", rule);
-            return false;
-        }
-        psTrace ("psModules.camera", 6, "looking for detrend (%s, %s)\n", file->detrend->detID, classId);
-        psFree (file->filename);
-
-        file->filename = pmDetrendFile(file->detrend->detID, classId, config);
-        if (file->filename == NULL) {
-            psError(PS_ERR_IO, false, "failed to find a valid detrend image for detID %s : classID %s\n", file->detrend->detID, classId);
-            psFree (classId);
-            return false;
-        }
-
-        psTrace ("psModules.camera", 6, "got detrend file %s\n", file->filename);
-        psFree (classId);
+    psFree(file->filename);
+    file->filename = pmFPAfileName(file, view, config);
+    if (!file->filename) {
+        psError(PS_ERR_IO, true, "Unable to determine filename");
+        return false;
     }
 
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/camera/pmFPAfileIO.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/camera/pmFPAfileIO.h	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/camera/pmFPAfileIO.h	(revision 23594)
@@ -15,4 +15,7 @@
 /// @addtogroup Camera Camera Layout
 /// @{
+
+// Determine appropriate file name
+psString pmFPAfileName(const pmFPAfile *file, const pmFPAview *view, pmConfig *config);
 
 // open the real file corresponding to the given pmFPAfile appropriate to the current view
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/concepts/pmConceptsStandard.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/concepts/pmConceptsStandard.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/concepts/pmConceptsStandard.c	(revision 23594)
@@ -606,4 +606,78 @@
 }
 
+// Get the current value of a concept
+static psMetadataItem *conceptGet(const pmFPA *fpa, // FPA of interest
+                                  const pmChip *chip, // Chip of interest, or NULL
+                                  const pmCell *cell, // Cell of interest, or NULL
+                                  const char *name // Concept name
+    )
+{
+    psMetadataItem *item = NULL;        // Item with time system
+    if (cell) {
+        item = psMetadataLookup(cell->concepts, name);
+    }
+    if (!item && chip) {
+        item = psMetadataLookup(chip->concepts, name);
+    }
+    if (!item && fpa) {
+        item = psMetadataLookup(fpa->concepts, name);
+    }
+    if (!item) {
+        psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find %s in concepts", name);
+        return NULL;
+    }
+    return item;
+}
+
+// Determine the corresponding TIMESYS for one of the TIME concepts
+static psTimeType conceptGetTimesysForTime(const char *name, // Concept name ("CELL.TIME" or "FPA.TIME")
+                                           const pmFPA *fpa, // FPA of interest
+                                           const pmChip *chip, // Chip of interest, or NULL
+                                           const pmCell *cell // Cell of interest, or NULL
+                                           )
+{
+    assert(name);
+
+    psString timesysName = psStringCopy(name); // e.g., "CELL.TIME" --> "CELL.TIMESYS"
+    psStringSubstitute(&timesysName, "TIMESYS", "TIME");
+    psMetadataItem *item = conceptGet(fpa, chip, cell, timesysName); // Time system
+    psFree(timesysName);
+
+    if (!item || item->type != PS_TYPE_S32) {
+        psWarning("Unable to find %s --- assuming UTC", timesysName);
+        return PS_TIME_UTC;
+    }
+    return item->data.S32;
+}
+
+// Set the corresponding TIMESYS for one of the TIME concepts
+static bool conceptSetTimesysForTime(const char *name, // Concept name ("CELL.TIME" or "FPA.TIME")
+                                     const pmFPA *fpa, // FPA of interest
+                                     const pmChip *chip, // Chip of interest, or NULL
+                                     const pmCell *cell, // Cell of interest, or NULL
+                                     psTimeType timeSys // The time system value
+                                     )
+{
+    assert(name);
+
+    psString timesysName = psStringCopy(name); // e.g., "CELL.TIME" --> "CELL.TIMESYS"
+    psStringSubstitute(&timesysName, "TIMESYS", "TIME");
+    psMetadataItem *item = conceptGet(fpa, chip, cell, timesysName); // Time system
+    psFree(timesysName);
+
+    if (!item) {
+        psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find %s in concepts when setting %s\n",
+                timesysName, name);
+        return false;
+    }
+
+    if (item->data.S32 != -1 && item->data.S32 != timeSys) {
+        psWarning("Time system is set to %x; but should be %x", item->data.S32, timeSys);
+    }
+
+    item->data.S32 = timeSys;
+
+    return true;
+}
 
 psMetadataItem *p_pmConceptParse_TIMESYS(const psMetadataItem *concept,
@@ -622,5 +696,5 @@
     if (concept->type != PS_DATA_STRING || strlen(sys) <= 0) {
         // XXX is this too low verbosity?
-        psLogMsg ("psModules.concepts", PS_LOG_DETAIL, "Can't interpret %s --- assuming UTC.\n", pattern->name);
+        psWarning("Can't interpret %s --- assuming UTC.", pattern->name);
     } else if (strcasecmp(sys, "TAI") == 0) {
         timeSys = PS_TIME_TAI;
@@ -633,5 +707,11 @@
     } else {
         // XXX is this too low verbosity?
-        psLogMsg ("psModules.concepts", PS_LOG_DETAIL, "Can't interpret %s --- assuming UTC.\n", pattern->name);
+        psWarning("Can't interpret %s --- assuming UTC.", pattern->name);
+    }
+
+    psMetadataItem *old = conceptGet(fpa, chip, cell, pattern->name); // Old value
+    if (old && old->data.S32 != -1 && old->data.S32 != timeSys) {
+        psWarning("%s is already set (%x) and not consistent with new value (%x)",
+                  pattern->name, old->data.S32, timeSys);
     }
 
@@ -639,35 +719,5 @@
 }
 
-// Determine the corresponding TIMESYS for one of the TIME concepts
-static psTimeType conceptGetTimesysForTime(const char *name, // Concept name ("CELL.TIME" or "FPA.TIME")
-                                           const pmFPA *fpa, // FPA of interest
-                                           const pmChip *chip, // Chip of interest, or NULL
-                                           const pmCell *cell // Cell of interest, or NULL
-                                           )
-{
-    assert(name);
-
-    psString timesysName = psStringCopy(name); // e.g., "CELL.TIME" --> "CELL.TIMESYS"
-    psStringSubstitute(&timesysName, "TIMESYS", "TIME");
-    bool mdok = false;                  // Result of MD lookup
-    psTimeType timeSys = 0xFFFFFFFF;    // The time system
-    if (cell) {
-        timeSys = psMetadataLookupS32(&mdok, cell->concepts, timesysName);
-    }
-    if (!mdok && chip) {
-        timeSys = psMetadataLookupS32(&mdok, chip->concepts, timesysName);
-    }
-    if (!mdok && fpa) {
-        timeSys = psMetadataLookupS32(&mdok, fpa->concepts, timesysName);
-    }
-    if (!mdok || (timeSys == 0xFFFFFFFF)) {
-        psWarning("Unable to find %s in concepts when parsing %s --- assuming UTC.\n",
-                  timesysName, name);
-        timeSys = PS_TIME_UTC;
-    }
-    psFree(timesysName);
-
-    return timeSys;
-}
+
 
 psMetadataItem *p_pmConceptParse_TIME(const psMetadataItem *concept,
@@ -896,5 +946,9 @@
     }
 
-    time->type = timeSys;
+    if (jdTime || mjdTime) {
+        conceptSetTimesysForTime(pattern->name, fpa, chip, cell, PS_TIME_TAI);
+    } else {
+        time->type = timeSys;
+    }
 
     psMetadataItem *item = psMetadataItemAllocPtr(pattern->name, PS_DATA_TIME, pattern->comment, time);
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/concepts/pmConceptsWrite.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/concepts/pmConceptsWrite.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/concepts/pmConceptsWrite.c	(revision 23594)
@@ -62,6 +62,5 @@
         while ((cItem = psListGetAndIncrement(cIter))) {
             if (cItem->type != PS_DATA_STRING) {
-                psWarning("psMetadataItem from list is of type %x instead of "
-                         "%x (PS_DATA_STRING) --- can't interpret.\n", cItem->type, PS_DATA_STRING);
+		psWarning("psMetadataItem from list is of type %x instead of %x (PS_DATA_STRING) --- can't interpret.\n", cItem->type, PS_DATA_STRING);
                 psFree(cIter);
                 psFree(sIter);
@@ -183,5 +182,5 @@
         }
     default:
-        psWarning("Type of %s is not suitable for a FITS header --- not added.\n",
+      psWarning("Type of %s is not suitable for a FITS header --- not added.\n",
                  item->name);
         return false;
@@ -226,4 +225,35 @@
 }
 
+// Return the camera format appropriate for a focal plane hierarchy
+static psMetadata *conceptsCameraFormat(const pmFPA *fpa, const pmChip *chip, const pmCell *cell)
+{
+    pmHDU *hdu = pmHDUGetLowest(fpa, chip, cell); // The HDU at the lowest level
+    if (!hdu) {
+        return NULL;
+    }
+    return hdu->format;
+}
+
+// Return the DATABASE metadata from the format
+static psMetadata *conceptsDatabase(const psMetadata *format)
+{
+    bool mdok;                          // Status of MD lookup
+    return psMetadataLookupMetadata(&mdok, format, "DEFAULTS");
+}
+
+// Return the TRANSLATION metadata from the format
+static psMetadata *conceptsTranslation(const psMetadata *format)
+{
+    bool mdok;                          // Status of MD lookup
+    return psMetadataLookupMetadata(&mdok, format, "TRANSLATION");
+}
+
+// Return the DEFAULTS metadata from the format
+static psMetadata *conceptsDefaults(const psMetadata *format)
+{
+    bool mdok;                          // Status of MD lookup
+    return psMetadataLookupMetadata(&mdok, format, "DEFAULTS");
+}
+
 
 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -231,8 +261,7 @@
 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
-bool p_pmConceptsWriteToCells(const psMetadata *specs, const pmCell *cell, const psMetadata *concepts)
-{
-    PS_ASSERT_PTR_NON_NULL(specs, false);
-    PS_ASSERT_PTR_NON_NULL(concepts, false);
+bool p_pmConceptWriteToCells(const pmCell *cell, const pmConceptSpec *spec,
+                             const psMetadataItem *conceptItem, const psMetadata *format)
+{
     if (!cell) {
         return false;
@@ -242,253 +271,280 @@
     }
 
-    pmHDU *hdu = pmHDUGetLowest(NULL, NULL, cell); // The HDU at the lowest level
+    if (!format) {
+        format = conceptsCameraFormat(NULL, NULL, cell);
+        if (!format) {
+            return false;
+        }
+    }
+
+    psMetadataItem *cameraItem = psMetadataLookup(cell->config, conceptItem->name); // Version in the config
+    if (!cameraItem) {
+        return false;
+    }
+
+    psString nameSource = NULL; // String with the concept name and ".SOURCE" added
+    psStringAppend(&nameSource, "%s.SOURCE", conceptItem->name);
+    bool mdok = true;       // Status of MD lookup
+    psString source = psMetadataLookupStr(&mdok, cell->config, nameSource); // The source
+    if (mdok && strlen(source) > 0) {
+        psTrace("psModules.concepts", 8, "%s is %s\n", nameSource, source);
+        if (strcasecmp(source, "HEADER") == 0) {
+            if (cameraItem->type != PS_DATA_STRING) {
+                psWarning("Concept %s is specified by header, but is not of type STR --- ignored.",
+                          conceptItem->name);
+                psFree(nameSource);
+                return false;
+            }
+
+            // Formatted version
+            psMetadataItem *formatted = conceptFormat(spec, conceptItem, PM_CONCEPT_SOURCE_HEADER,
+                                                      format, NULL, NULL, cell);
+            if (!formatted) {
+                psFree(nameSource);
+                return true;
+            }
+
+            psTrace("psModules.concepts", 8, "Writing %s to header %s\n",
+                    conceptItem->name, cameraItem->data.str);
+            pmHDU *hdu = pmHDUGetLowest(NULL, NULL, cell); // Header data unit
+            if (!hdu) {
+                psError(PS_ERR_UNEXPECTED_NULL, false,
+                        "Unable to find HDU to write concept %s", conceptItem->name);
+                return false;
+            }
+            writeHeader(hdu, cameraItem->data.V, formatted);
+            psFree(formatted);
+        } else if (strcasecmp(source, "VALUE") == 0) {
+            // Formatted version
+            psMetadataItem *formatted = conceptFormat(spec, conceptItem, PM_CONCEPT_SOURCE_CELLS,
+                                                      format, NULL, NULL, cell);
+            if (!formatted) {
+                psFree(nameSource);
+                return true;
+            }
+
+            psTrace("psModules.concepts", 8, "Checking %s against camera format.\n", conceptItem->name);
+            if (!compareConcepts(formatted, cameraItem)) {
+                psWarning("Concept %s is specified by value in the camera format, but the values don't match",
+                          conceptItem->name);
+            }
+            psFree(formatted);
+        } else {
+            psWarning("Concept source %s isn't HEADER or VALUE --- can't write", nameSource);
+        }
+    } else {
+        // Assume it's specified by value
+        psMetadataItem *formatted = conceptFormat(spec, conceptItem, PM_CONCEPT_SOURCE_CELLS,
+                                                  format, NULL, NULL, cell);
+        if (!formatted) {
+            psFree(nameSource);
+            return true;
+        }
+
+        if (!compareConcepts(formatted, cameraItem)) {
+            psWarning("Concept %s is specified by value in the camera format, but the values don't match.",
+                      conceptItem->name);
+        }
+        psFree(formatted);
+    }
+    psFree(nameSource);
+
+    return true;
+}
+
+bool p_pmConceptWriteToDefaults(const pmFPA *fpa, const pmChip *chip, const pmCell *cell,
+                                const pmConceptSpec *spec, const psMetadataItem *conceptItem,
+                                const psMetadata *format, const psMetadata *defaults)
+{
+    if (!format) {
+        format = conceptsCameraFormat(fpa, chip, cell);
+        if (!format) {
+            return false;
+        }
+    }
+    if (!defaults) {
+        defaults = conceptsDefaults(format);
+        if (!defaults) {
+            return false;
+        }
+    }
+
+    psMetadataItem *defaultItem = p_pmConceptsReadSingleFromDefaults(conceptItem->name, defaults,
+                                                                     fpa, chip, cell);
+    if (!defaultItem) {
+        return false;
+    }
+    psMetadataItem *formatted = conceptFormat(spec, conceptItem, PM_CONCEPT_SOURCE_DEFAULTS,
+                                              format, fpa, chip, cell);
+    if (!formatted) {
+        return true;
+    }
+
+    if (strcmp(defaultItem->name, conceptItem->name) != 0) {
+        // Correct the name to match the concept name
+        defaultItem = psMetadataItemCopy(defaultItem);
+        psFree(defaultItem->name);
+        defaultItem->name = psStringCopy(conceptItem->name);
+    } else {
+        psMemIncrRefCounter(defaultItem);
+    }
+
+    if (!compareConcepts(formatted, defaultItem)) {
+        psWarning("Concept %s is specified by the DEFAULTS in the camera format, but the values don't match.",
+                  conceptItem->name);
+    }
+    psFree(defaultItem);
+    psFree(formatted);
+
+    return true;
+}
+
+
+bool p_pmConceptWriteToHeader(const pmFPA *fpa, const pmChip *chip, const pmCell *cell,
+                              const pmConceptSpec *spec, const psMetadataItem *conceptItem,
+                              const psMetadata *format, const psMetadata *translation)
+{
+    if (!format) {
+        format = conceptsCameraFormat(fpa, chip, cell);
+        if (!format) {
+            return false;
+        }
+    }
+    if (!translation) {
+        translation = conceptsTranslation(format);
+        if (!translation) {
+            return false;
+        }
+    }
+
+    psMetadataItem *headerItem = psMetadataLookup(translation, conceptItem->name); // How to format for header
+    if (!headerItem) {
+        return false;
+    }
+    if (headerItem->type == PS_DATA_METADATA) {
+        // This is a menu
+        psTrace("psModules.concepts", 5, "%s is of type METADATA.\n", conceptItem->name);
+        headerItem = p_pmConceptsDepend(conceptItem->name, headerItem->data.md, translation, fpa, chip, cell);
+        if (!headerItem) {
+            return false;
+        }
+    }
+    if (headerItem->type != PS_DATA_STRING) {
+        psWarning("TRANSLATION keyword for concept %s isn't of type STR --- ignored.", conceptItem->name);
+        return false;
+    }
+    psTrace("psModules.concepts", 3, "Writing %s to header %s\n", conceptItem->name, headerItem->data.str);
+    psMetadataItem *formatted = conceptFormat(spec, conceptItem, PM_CONCEPT_SOURCE_HEADER,
+                                              format, fpa, chip, cell);
+    if (!formatted) {
+        // Found it, but it doesn't need to be written
+        return true;
+    }
+
+    pmHDU *hdu = pmHDUGetLowest(fpa, chip, cell); // HDU to which to write
     if (!hdu) {
-        return false;
-    }
-    psMetadata *cameraFormat = hdu->format; // The camera format
-    psMetadataIterator *specsIter = psMetadataIteratorAlloc(specs, PS_LIST_HEAD, NULL); // Iterator
-    psMetadataItem *specItem = NULL;    // Item from the specs metadata
-    while ((specItem = psMetadataGetAndIncrement(specsIter))) {
-        pmConceptSpec *spec = specItem->data.V; // The specification
-        psString name = specItem->name; // The concept name
-        psMetadataItem *cameraItem = psMetadataLookup(cell->config, name); // The concept from the camera,
-        // or NULL
-        if (cameraItem) {
-            // Grab the concept
-            psMetadataItem *conceptItem = psMetadataLookup(concepts, name); // The concept
-
-            psString nameSource = NULL; // String with the concept name and ".SOURCE" added
-            psStringAppend(&nameSource, "%s.SOURCE", name);
-            bool mdok = true;       // Status of MD lookup
-            psString source = psMetadataLookupStr(&mdok, cell->config, nameSource); // The source
-            if (mdok && strlen(source) > 0) {
-                psTrace("psModules.concepts", 8, "%s is %s\n", nameSource, source);
-                if (strcasecmp(source, "HEADER") == 0) {
-                    if (cameraItem->type != PS_DATA_STRING) {
-                        psWarning("Concept %s is specified by header, but is not "
-                                 "of type STR --- ignored.\n", conceptItem->name);
-                        continue;
-                    }
-
-                    // Formatted version
-                    psMetadataItem *formatted = conceptFormat(spec, conceptItem, PM_CONCEPT_SOURCE_HEADER,
-                                                              cameraFormat, NULL, NULL, cell);
-                    if (!formatted) {
-                        continue;
-                    }
-
-                    psTrace("psModules.concepts", 8, "Writing %s to header %s\n", name, cameraItem->data.str);
-                    writeHeader(hdu, cameraItem->data.V, formatted);
-                    psFree(formatted);
-                } else if (strcasecmp(source, "VALUE") == 0) {
-                    // Formatted version
-                    psMetadataItem *formatted = conceptFormat(spec, conceptItem, PM_CONCEPT_SOURCE_CELLS,
-                                                              cameraFormat, NULL, NULL, cell);
-                    if (!formatted) {
-                        continue;
-                    }
-
-                    psTrace("psModules.concepts", 8, "Checking %s against camera format.\n", name);
-                    if (! compareConcepts(formatted, cameraItem)) {
-                        psWarning("Concept %s is specified by value in the camera "
-                                 "format, but the values don't match.\n", name);
-                    }
-                    psFree(formatted);
-                } else {
-                    psWarning("Concept source %s isn't HEADER or VALUE --- can't "
-                             "write\n", nameSource);
-                }
-            } else {
-                // Assume it's specified by value
-                psMetadataItem *formatted = conceptFormat(spec, conceptItem, PM_CONCEPT_SOURCE_CELLS,
-                                                          cameraFormat, NULL, NULL, cell);
-                if (!formatted) {
-                    continue;
-                }
-
-                if (! compareConcepts(formatted, cameraItem)) {
-                    psWarning("Concept %s is specified by value in the camera "
-                             "format, but the values don't match.\n", name);
-                }
-                psFree(formatted);
-            }
-            psFree(nameSource);
-        }
-
-    }
-    psFree(specsIter);
+        psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to find HDU to write concept %s", conceptItem->name);
+        return false;
+    }
+    writeHeader(hdu, headerItem->data.V, formatted);
+    psFree(formatted);
+
     return true;
 }
 
-bool p_pmConceptsWriteToDefaults(const psMetadata *specs, const pmFPA *fpa, const pmChip *chip,
-                                 const pmCell *cell, const psMetadata *concepts)
-{
-    PS_ASSERT_PTR_NON_NULL(specs, false);
-    PS_ASSERT_PTR_NON_NULL(concepts, false);
-
-    pmHDU *hdu = pmHDUGetLowest(fpa, chip, cell); // The HDU at the lowest level
-    if (!hdu) {
-        return false;
-    }
-    psMetadata *cameraFormat = hdu->format; // The camera format
-    bool mdok = true;                   // Status of MD lookup
-    psMetadata *defaults = psMetadataLookupMetadata(&mdok, cameraFormat, "DEFAULTS"); // The DEFAULTS spec
-    if (!mdok || !defaults) {
-        return false;
-    }
-
-    psMetadataIterator *specsIter = psMetadataIteratorAlloc(specs, PS_LIST_HEAD, NULL); // Iterator
-    psMetadataItem *specItem = NULL;    // Item from the specs metadata
-    while ((specItem = psMetadataGetAndIncrement(specsIter))) {
-        pmConceptSpec *spec = specItem->data.V; // The specification
-        psString name = specItem->name; // The concept name
-
-        psMetadataItem *defaultItem = p_pmConceptsReadSingleFromDefaults(name, defaults, fpa, chip, cell);
-        if (!defaultItem) {
-            continue;
-        }
-        psMetadataItem *conceptItem = psMetadataLookup(concepts, name); // The item from the concepts
-        psMetadataItem *formatted = conceptFormat(spec, conceptItem, PM_CONCEPT_SOURCE_DEFAULTS,
-                                                  cameraFormat, fpa, chip, cell);
-        if (!formatted) {
-            continue;
-        }
-
-        if (strcmp(defaultItem->name, name) != 0) {
-            // Correct the name to match the concept name
-            defaultItem = psMetadataItemCopy(defaultItem);
-            psFree(defaultItem->name);
-            defaultItem->name = psStringCopy(name);
-        } else {
-            psMemIncrRefCounter(defaultItem);
-        }
-
-        if (!compareConcepts(formatted, defaultItem)) {
-            psWarning("Concept %s is specified by the DEFAULTS in the camera "
-                      "format, but the values don't match.\n", name);
-        }
-        psFree(defaultItem);
-        psFree(formatted);
-    }
-    psFree(specsIter);
+bool p_pmConceptWriteToDatabase(const pmFPA *fpa, const pmChip *chip, const pmCell *cell,
+                                pmConfig *config, const pmConceptSpec *spec,
+                                const psMetadataItem *conceptItem, const psMetadata *format,
+                                const psMetadata *database)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+#ifndef HAVE_PSDB
+    return false;
+#else
+
+    if (!format) {
+        format = conceptsCameraFormat(fpa, chip, cell);
+        if (!format) {
+            return false;
+        }
+    }
+    if (!database) {
+        database = conceptsDatabase(format);
+        if (!database) {
+            return false;
+        }
+    }
+
+    psMetadataItem *dbItem = p_pmConceptsReadSingleFromDatabase(conceptItem->name, database, config,
+                                                                fpa, chip, cell); // Database version
+    if (!dbItem) {
+        return false;
+    }
+
+    psMetadataItem *formatted = conceptFormat(spec, conceptItem, PM_CONCEPT_SOURCE_DATABASE,
+                                              format, fpa, chip, cell);
+    if (!formatted) {
+        return false;
+    }
+
+    if (strcmp(dbItem->name, conceptItem->name) != 0) {
+        // Correct the name to match the concept name
+        dbItem = psMetadataItemCopy(dbItem);
+        psFree(dbItem->name);
+        dbItem->name = psStringCopy(conceptItem->name);
+    } else {
+        psMemIncrRefCounter(dbItem);
+    }
+
+    if (!compareConcepts(formatted, dbItem)) {
+        psWarning("Concept %s is specified by the DATABASE in the camera "
+                  "format, but the values don't match.\n", conceptItem->name);
+    }
+    psFree(dbItem);
+    psFree(formatted);
+
     return true;
-}
-
-
-bool p_pmConceptsWriteToHeader(const psMetadata *specs, const pmFPA *fpa, const pmChip *chip,
-                               const pmCell *cell, const psMetadata *concepts)
-{
-    PS_ASSERT_PTR_NON_NULL(specs, false);
-    PS_ASSERT_PTR_NON_NULL(concepts, false);
-
-    pmHDU *hdu = pmHDUGetLowest(fpa, chip, cell); // The HDU at the lowest level
-    if (!hdu) {
-        return false;
-    }
-    psMetadata *cameraFormat = hdu->format; // The camera format
-    bool mdok = true;                   // Status of MD lookup
-    psMetadata *translation = psMetadataLookupMetadata(&mdok, cameraFormat, "TRANSLATION"); // The TRANSLATION spec
-    if (!mdok || !translation) {
-        return false;
-    }
-
-    psMetadataIterator *specsIter = psMetadataIteratorAlloc(specs, PS_LIST_HEAD, NULL); // Iterator
-    psMetadataItem *specItem = NULL;    // Item from the specs metadata
-    while ((specItem = psMetadataGetAndIncrement(specsIter))) {
-        pmConceptSpec *spec = specItem->data.V; // The specification
-        psString name = specItem->name; // The concept name
-        psMetadataItem *headerItem = psMetadataLookup(translation, name); // The item from the TRANSLATION
-        if (!headerItem) {
-            continue;
-        }
-        if (headerItem->type == PS_DATA_METADATA) {
-            // This is a menu
-            psTrace("psModules.concepts", 5, "%s is of type METADATA.\n", name);
-            headerItem = p_pmConceptsDepend(name, headerItem->data.md, translation, fpa, chip, cell);
-            if (!headerItem) {
-                continue;
-            }
-        }
-        if (headerItem->type != PS_DATA_STRING) {
-            psWarning("TRANSLATION keyword for concept %s isn't of type STR --- ignored.", name);
-            continue;
-        }
-        psTrace("psModules.concepts", 3, "Writing %s to header %s\n", name, headerItem->data.str);
-        psMetadataItem *conceptItem = psMetadataLookup(concepts, name); // The item from the concepts
-        psMetadataItem *formatted = conceptFormat(spec, conceptItem, PM_CONCEPT_SOURCE_HEADER,
-                                                  cameraFormat, fpa, chip, cell);
-        if (!formatted) {
-            continue;
-        }
-        writeHeader(hdu, headerItem->data.V, formatted);
-        psFree(formatted);
-    }
-    psFree(specsIter);
+#endif
+}
+
+
+bool pmConceptWriteSingle(const pmFPA *fpa, const pmChip *chip, const pmCell *cell,
+                          pmConfig *config, const psMetadataItem *conceptItem)
+{
+    pmConceptsInit();
+
+    psMetadata *format = conceptsCameraFormat(fpa, chip, cell); // Camera format
+    if (!format) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to retrieve camera format.");
+        return false;
+    }
+
+    const char *name = conceptItem->name; // Name of concept
+
+    psMetadata *conceptsFPA = pmConceptsSpecs(PM_FPA_LEVEL_FPA); // Concept specifications for FPA
+    bool mdok;                          // Status of MD lookup
+    pmConceptSpec *spec = psMetadataLookupPtr(&mdok, conceptsFPA, name); // Concept specification of interest
+    if (!spec) {
+        psMetadata *conceptsChip = pmConceptsSpecs(PM_FPA_LEVEL_CHIP); // Concept specifications for Chip
+        spec = psMetadataLookupPtr(&mdok, conceptsChip, name);
+        if (!spec) {
+            psMetadata *conceptsCell = pmConceptsSpecs(PM_FPA_LEVEL_CELL); // Concept specifications for Cell
+            spec = psMetadataLookupPtr(&mdok, conceptsCell, name);
+            if (!spec) {
+                psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find specification for concept %s", name);
+                return false;
+            }
+        }
+    }
+
+    if (!p_pmConceptWriteToCells(cell, spec, conceptItem, format) &&
+        !p_pmConceptWriteToDefaults(fpa, chip, cell, spec, conceptItem, format, NULL) &&
+        !p_pmConceptWriteToHeader(fpa, chip, cell, spec, conceptItem, format, NULL) &&
+        !p_pmConceptWriteToDatabase(fpa, chip, cell, config, spec, conceptItem, format, NULL)) {
+        return false;
+    }
     return true;
 }
-
-// XXX Warning: This code has not been tested at all
-bool p_pmConceptsWriteToDatabase(const psMetadata *specs, const pmFPA *fpa, const pmChip *chip,
-                                 const pmCell *cell, pmConfig *config, const psMetadata *concepts)
-{
-    PS_ASSERT_PTR_NON_NULL(specs, false);
-    PS_ASSERT_PTR_NON_NULL(concepts, false);
-    PS_ASSERT_PTR_NON_NULL(config, false);
-
-    #ifndef HAVE_PSDB
-    return false;
-    #else
-
-    pmHDU *hdu = pmHDUGetLowest(fpa, chip, cell); // The HDU at the lowest level
-    if (!hdu) {
-        return false;
-    }
-    psMetadata *cameraFormat = hdu->format; // The camera format
-    bool mdok = true;                   // Status of MD lookup
-    psMetadata *database = psMetadataLookupMetadata(&mdok, cameraFormat, "DATABASE"); // The DATABASE spec
-    if (!mdok || !database) {
-        return false;
-    }
-    psMetadataIterator *specsIter = psMetadataIteratorAlloc(specs, PS_LIST_HEAD, NULL); // Iterator
-    psMetadataItem *specItem = NULL;    // Item from the specs metadata
-    while ((specItem = psMetadataGetAndIncrement(specsIter))) {
-        pmConceptSpec *spec = specItem->data.V; // The specification
-        psString name = specItem->name; // The concept name
-
-        psMetadataItem *dbItem = p_pmConceptsReadSingleFromDatabase(name, database, config, fpa, chip, cell);
-        if (!dbItem) {
-            continue;
-        }
-
-        psMetadataItem *conceptItem = psMetadataLookup(concepts, name); // The item from the concepts
-        psMetadataItem *formatted = conceptFormat(spec, conceptItem, PM_CONCEPT_SOURCE_DATABASE,
-                                                  cameraFormat, fpa, chip, cell);
-        if (!formatted) {
-            continue;
-        }
-
-        if (strcmp(dbItem->name, name) != 0) {
-            // Correct the name to match the concept name
-            dbItem = psMetadataItemCopy(dbItem);
-            psFree(dbItem->name);
-            dbItem->name = psStringCopy(name);
-        } else {
-            psMemIncrRefCounter(dbItem);
-        }
-
-        if (!compareConcepts(formatted, dbItem)) {
-            psWarning("Concept %s is specified by the DATABASE in the camera "
-                      "format, but the values don't match.\n", name);
-        }
-        psFree(dbItem);
-        psFree(formatted);
-    }
-    psFree(specsIter);
-    return true;
-    #endif
-}
-
-
-
 
 
@@ -498,5 +554,4 @@
                           const pmChip *chip, // The chip
                           const pmCell *cell, // The cell
-                          pmConceptSource source, // The source of the concepts to write
                           pmConfig *config, // Configuration
                           psMetadata *concepts // The concepts to write out
@@ -508,18 +563,32 @@
     pmConceptsInit();
 
-    psTrace("psModules.concepts", 3, "Writing concepts (%p %p %p): %d\n", fpa, chip, cell, source);
-
-    if (source & PM_CONCEPT_SOURCE_CELLS) {
-        p_pmConceptsWriteToCells(*specs, cell, concepts);
-    }
-    if (source & PM_CONCEPT_SOURCE_DEFAULTS) {
-        p_pmConceptsWriteToDefaults(*specs, fpa, chip, cell, concepts);
-    }
-    if (source & (PM_CONCEPT_SOURCE_PHU | PM_CONCEPT_SOURCE_HEADER)) {
-        p_pmConceptsWriteToHeader(*specs, fpa, chip, cell, concepts);
-    }
-    if (source & PM_CONCEPT_SOURCE_DATABASE) {
-        p_pmConceptsWriteToDatabase(*specs, fpa, chip, cell, config, concepts);
-    }
+    psTrace("psModules.concepts", 3, "Writing concepts (%p %p %p)\n", fpa, chip, cell);
+
+    psMetadata *format = conceptsCameraFormat(fpa, chip, cell); // Camera format
+    if (!format) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to retrieve camera format.");
+        return false;
+    }
+
+    psMetadata *defaults = conceptsDefaults(format); // DEFAULTS configuration
+    psMetadata *translation = conceptsTranslation(format); // TRANSLATION configuration
+    psMetadata *database = conceptsDatabase(format); // DATABASE configuration
+
+    psMetadataIterator *iter = psMetadataIteratorAlloc(*specs, PS_LIST_HEAD, NULL); // Iterator
+    psMetadataItem *item = NULL;    // Item from the specs metadata
+    while ((item = psMetadataGetAndIncrement(iter))) {
+        pmConceptSpec *spec = item->data.V; // The specification
+        psString name = item->name; // The concept name
+
+        psMetadataItem *concept = psMetadataLookup(concepts, name); // Concept to write
+
+        if (!p_pmConceptWriteToCells(cell, spec, concept, format) &&
+            !p_pmConceptWriteToDefaults(fpa, chip, cell, spec, concept, format, defaults) &&
+            !p_pmConceptWriteToHeader(fpa, chip, cell, spec, concept, format, translation) &&
+            !p_pmConceptWriteToDatabase(fpa, chip, cell, config, spec, concept, format, database)) {
+            psTrace("psModules.concepts", 1, "Unable to write concept %s to any output", name);
+        }
+    }
+    psFree(iter);
 
     return true;
@@ -527,10 +596,10 @@
 
 
-bool pmConceptsWriteFPA(const pmFPA *fpa, pmConceptSource source, bool propagateDown, pmConfig *config)
+bool pmConceptsWriteFPA(const pmFPA *fpa, bool propagateDown, pmConfig *config)
 {
     PS_ASSERT_PTR_NON_NULL(fpa, false);
     psMetadata *conceptsFPA = pmConceptsSpecs(PM_FPA_LEVEL_FPA); // Concept specifications
     psTrace("psModules.concepts", 5, "Writing FPA concepts: %p %p\n", conceptsFPA, fpa->concepts);
-    bool success = conceptsWrite(&conceptsFPA, fpa, NULL, NULL, source, config, fpa->concepts);
+    bool success = conceptsWrite(&conceptsFPA, fpa, NULL, NULL, config, fpa->concepts);
     if (propagateDown) {
         psArray *chips = fpa->chips;        // Array of chips
@@ -538,5 +607,5 @@
             pmChip *chip = chips->data[i];  // Chip of interest
             if (chip && !chip->hdu) {
-                success &= pmConceptsWriteChip(chip, source, false, true, config);
+                success &= pmConceptsWriteChip(chip, false, true, config);
             }
         }
@@ -546,6 +615,5 @@
 
 
-bool pmConceptsWriteChip(const pmChip *chip, pmConceptSource source, bool propagateUp,
-                         bool propagateDown, pmConfig *config)
+bool pmConceptsWriteChip(const pmChip *chip, bool propagateUp, bool propagateDown, pmConfig *config)
 {
     PS_ASSERT_PTR_NON_NULL(chip, false);
@@ -553,8 +621,8 @@
     psTrace("psModules.concepts", 5, "Writing chip concepts: %p %p\n", conceptsChip, chip->concepts);
     pmFPA *fpa = chip->parent;          // FPA to which the chip belongs
-    bool success = conceptsWrite(&conceptsChip, fpa, chip, NULL, source, config, chip->concepts);
+    bool success = conceptsWrite(&conceptsChip, fpa, chip, NULL, config, chip->concepts);
     if (propagateUp && !fpa->hdu) {
         psMetadata *conceptsFPA = pmConceptsSpecs(PM_FPA_LEVEL_FPA); // Concept specifications
-        success &= conceptsWrite(&conceptsFPA, fpa, chip, NULL, source, config, fpa->concepts);
+        success &= conceptsWrite(&conceptsFPA, fpa, chip, NULL, config, fpa->concepts);
     }
     if (propagateDown) {
@@ -563,5 +631,5 @@
             pmCell *cell = cells->data[i];  // Cell of interest
             if (cell && !cell->hdu) {
-                success &= pmConceptsWriteCell(cell, source, false, config);
+                success &= pmConceptsWriteCell(cell, false, config);
             }
         }
@@ -571,5 +639,5 @@
 
 
-bool pmConceptsWriteCell(const pmCell *cell, pmConceptSource source, bool propagateUp, pmConfig *config)
+bool pmConceptsWriteCell(const pmCell *cell, bool propagateUp, pmConfig *config)
 {
     PS_ASSERT_PTR_NON_NULL(cell, false);
@@ -579,12 +647,12 @@
     pmFPA *fpa = chip->parent;          // FPA to which the chip belongs
 
-    bool success = conceptsWrite(&conceptsCell, fpa, chip, cell, source, config, cell->concepts);
+    bool success = conceptsWrite(&conceptsCell, fpa, chip, cell, config, cell->concepts);
     if (propagateUp) {
         if (!chip->hdu) {
             psMetadata *conceptsChip = pmConceptsSpecs(PM_FPA_LEVEL_CHIP); // Concept specifications
-            success &= conceptsWrite(&conceptsChip, fpa, chip, cell, source, config, chip->concepts);
+            success &= conceptsWrite(&conceptsChip, fpa, chip, cell, config, chip->concepts);
             if (!fpa->hdu) {
                 psMetadata *conceptsFPA = pmConceptsSpecs(PM_FPA_LEVEL_FPA); // Concept specifications
-                success &= conceptsWrite(&conceptsFPA, fpa, chip, cell, source, config, fpa->concepts);
+                success &= conceptsWrite(&conceptsFPA, fpa, chip, cell, config, fpa->concepts);
             }
         }
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/concepts/pmConceptsWrite.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/concepts/pmConceptsWrite.h	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/concepts/pmConceptsWrite.h	(revision 23594)
@@ -20,47 +20,62 @@
 /// @{
 
-/// "Write" concepts to (actually, check against) the camera format file's CELLS.
+/// "Write" concept to (actually, check against) the camera format file's CELLS.
 ///
 /// Examines the CELLS metadata in the camera format file for the current type of cell, and checks that the
-/// concepts as defined there match the ones defined in the cell.  A warning is produced if the concepts do
+/// concept as defined there match the ones defined in the cell.  A warning is produced if the concept does
 /// not match.
-bool p_pmConceptsWriteToCells(const psMetadata *specs, ///< The concept specifications
+bool p_pmConceptWriteToCells(const pmCell *cell, ///< The cell
+                             const pmConceptSpec *spec, ///< Concept specification
+                             const psMetadataItem *conceptItem, ///< Concept to write
+                             const psMetadata *format ///< Camera format, or NULL
+    );
+
+/// "Write" concept to (actually, check against) the camera format file's DEFAULTS.
+///
+/// Examines the DEFAULTS metadata in the camera format file, and checks that the concept as defined there
+/// match the one defined in the cell.  A warning is produced if the concept does not match.
+bool p_pmConceptWriteToDefaults(const pmFPA *fpa, ///< The FPA
+                                const pmChip *chip, ///< The chip
+                                const pmCell *cell, ///< The cell
+                                const pmConceptSpec *spec, ///< Concept specification
+                                const psMetadataItem *conceptItem,      ///< Concept to write
+                                const psMetadata *format, ///< Camera format, or NULL
+                                const psMetadata *defaults ///< DEFAULTS configuration, or NULL
+    );
+
+/// "Write" concept to (actually, add to, pending a later write) the FITS header.
+///
+/// Examines the FITS header TRANSLATION metadata in the camera format file, and writes concept to the
+/// appropriate FITS header(s) in the HDU, in preparation for a future write of the HDU.
+bool p_pmConceptWriteToHeader(const pmFPA *fpa, ///< The FPA
+                              const pmChip *chip, ///< The chip
                               const pmCell *cell, ///< The cell
-                              const psMetadata *concepts ///< The concepts
-                             );
+                              const pmConceptSpec *spec, ///< Concept specification
+                              const psMetadataItem *conceptItem, ///< Concept to write
+                              const psMetadata *format, ///< Camera format, or NULL
+                              const psMetadata *translation ///< TRANSLATION configuration, or NULL
+                              );
 
-/// "Write" concepts to (actually, check against) the camera format file's DEFAULTS.
+/// Write concept to the database.
 ///
-/// Examines the DEFAULTS metadata in the camera format file, and checks that the concepts as defined there
-/// match the ones defined in the cell.  A warning is produced if the concepts do not match.
-bool p_pmConceptsWriteToDefaults(const psMetadata *specs, ///< The concept specifications
-                                 const pmFPA *fpa, ///< The FPA
-                                 const pmChip *chip, ///< The chip
-                                 const pmCell *cell, ///< The cell
-                                 const psMetadata *concepts ///< The concepts
+/// Examines the DATABASE metadata in the camera format file, and writes (actually, check against)
+/// concept to the database.
+bool p_pmConceptWriteToDatabase(const pmFPA *fpa, ///< The FPA
+                                const pmChip *chip, ///< The chip
+                                const pmCell *cell, ///< The cell
+                                pmConfig *config, ///< Configuration
+                                const pmConceptSpec *spec, ///< Concept specification
+                                const psMetadataItem *conceptItem, ///< Concept to write
+                                const psMetadata *format, ///< Camera format, or NULL
+                                const psMetadata *database ///< DATABASE configuration, or NULL
                                 );
 
-/// "Write" concepts to (actually, add to, pending a later write) the FITS header.
-///
-/// Examines the FITS header TRANSLATION metadata in the camera format file, and writes concepts to the
-/// appropriate FITS headers in the HDU, in preparation for a future write of the HDU.
-bool p_pmConceptsWriteToHeader(const psMetadata *specs, ///< The concept specifications
-                               const pmFPA *fpa, ///< The FPA
-                               const pmChip *chip, ///< The chip
-                               const pmCell *cell, ///< The cell
-                               const psMetadata *concepts ///< The concepts
-                              );
 
-/// Write concepts to the database.
-///
-/// Examines the DATABASE metadata in the camera format file, and writes concepts to the database.
-/// Warning: This function has not been tested; use at your own risk.
-bool p_pmConceptsWriteToDatabase(const psMetadata *specs, ///< The concept specifications
-                                 const pmFPA *fpa, ///< The FPA
-                                 const pmChip *chip, ///< The chip
-                                 const pmCell *cell, ///< The cell
-                                 pmConfig *config,///< Configuration
-                                 const psMetadata *concepts ///< The concepts
-                                );
+bool pmConceptWriteSingle(const pmFPA *fpa, ///< The FPA
+                          const pmChip *chip, ///< The chip
+                          const pmCell *cell, ///< The cell
+                          pmConfig *config, ///< Configuration
+                          const psMetadataItem *concept ///< Concept to write
+    );
 
 
@@ -70,5 +85,4 @@
 /// written for all lower levels by iterating over the components.
 bool pmConceptsWriteFPA(const pmFPA *fpa,     ///< FPA for which to write concepts
-                        pmConceptSource source, ///< Source for concepts
                         bool propagateDown, ///< Propagate to lower levels?
                         pmConfig *config        ///< Configuration
@@ -80,5 +94,4 @@
 /// written for the FPA, and the cell level by iterating over the components.
 bool pmConceptsWriteChip(const pmChip *chip,  ///< Chip for which to write concepts
-                         pmConceptSource source, ///< Source for concepts
                          bool propagateUp,///< Propagate to higher levels?
                          bool propagateDown, ///< Propagate to lower levels?
@@ -92,5 +105,4 @@
 /// only the parent of this cell).
 bool pmConceptsWriteCell(const pmCell *cell,  ///< FPA for which to write concepts
-                         pmConceptSource source, ///< Source for concepts
                          bool propagateUp, ///< Propagate to higher levels?
                          pmConfig *config ///< Configuration
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/config/pmConfig.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/config/pmConfig.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/config/pmConfig.c	(revision 23594)
@@ -806,4 +806,10 @@
     pmConfigLoadRecipeOptions(argc, argv, config, "-Df");
     pmConfigLoadRecipeOptions(argc, argv, config, "-Db");
+
+    if (!pmConfigReadRecipes(config, PM_RECIPE_SOURCE_CL)) {
+        psError(PS_ERR_IO, false, "Failed to read recipes from command-line");
+        psFree(config);
+        return NULL;
+    }
 
     // Look for command-line options for files to replace
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/config/pmConfigCamera.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/config/pmConfigCamera.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/config/pmConfigCamera.c	(revision 23594)
@@ -159,7 +159,7 @@
 // Don't update these skycell concepts; last one MUST be 0 (i.e., NULL).
 const static char *skycellConceptsCell[] = { "CELL.BIASSEC", "CELL.TRIMSEC", "CELL.READDIR", "CELL.XPARITY",
-                                             "CELL.YPARITY", "CELL.X0", "CELL.Y0", 0 };
+                                             "CELL.YPARITY", "CELL.X0", "CELL.Y0", "CELL.TIMESYS", 0 };
 const static char *skycellConceptsChip[] = { "CHIP.XPARITY", "CHIP.YPARITY", 0 };
-const static char *skycellConceptsFPA[] = { 0 };
+const static char *skycellConceptsFPA[] = { "FPA.TIMESYS" };
 
 // What do we call the skycell concept in the FITS header?
@@ -332,4 +332,6 @@
         psMetadataAddS32(defaults, PS_LIST_TAIL, "CHIP.X0", 0, NULL, 0);
         psMetadataAddS32(defaults, PS_LIST_TAIL, "CELL.READDIR", 0, "Read direction (rows)", 1);
+        psMetadataAddStr(defaults, PS_LIST_TAIL, "CELL.TIMESYS", 0, "Time system", "TAI");
+        psMetadataAddStr(defaults, PS_LIST_TAIL, "FPA.TIMESYS", 0, "Time system", "TAI");
 
         psMetadataAddMetadata(format, PS_LIST_TAIL, "DEFAULTS", 0, "Default values for concepts", defaults);
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/config/pmConfigMask.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/config/pmConfigMask.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/config/pmConfigMask.c	(revision 23594)
@@ -10,17 +10,23 @@
 
 static pmConfigMaskInfo masks[] = {
-    { "DETECTOR",  NULL,       0x00, true  },   // Something is wrong with the detector
-    { "DARK",      "DETECTOR", 0x00, true  },   // Pixel doesn't dark-subtract properly
-    { "FLAT",      "DETECTOR", 0x01, true  },   // Pixel doesn't flat-field properly
-    { "BLANK",     "DETECTOR", 0x01, true  },   // Pixel doesn't contain valid data
-    { "RANGE",     NULL,       0x00, true  },   // Pixel is out-of-range of linearity
-    { "SAT",       "RANGE",    0x01, true  },   // Pixel is saturated
-    { "BAD",       "RANGE",    0x01, true  },   // Pixel is low
-    { "BAD.WARP",  NULL,       0x01, true  },   // Pixel is bad after convolution with a bad pixel
-    { "CR",        NULL,       0x00, true  },   // Pixel contains a cosmic ray
-    { "GHOST",     NULL,       0x00, true  },   // Pixel contains an optical ghost
-    { "POOR.WARP", NULL,       0x00, false },   // Pixel is poor after convolution with a bad pixel
-    // "LOW"  Pixel is low
-    // "CONV" Pixel is bad after convolution with a bad pixel
+    // Features of the detector
+    { "DETECTOR",  NULL,       0x01, true  }, // Something is wrong with the detector
+    { "FLAT",      "DETECTOR", 0x01, true  }, // Pixel doesn't flat-field properly
+    { "DARK",      "DETECTOR", 0x01, true  }, // Pixel doesn't dark-subtract properly
+    { "BLANK",     "DETECTOR", 0x01, true  }, // Pixel doesn't contain valid data
+    { "CTE",       "DETECTOR", 0x01, true  }, // Pixel has poor CTE
+    // Invalid signal ranges
+    { "SAT",       NULL,       0x02, true  }, // Pixel is saturated or non-linear
+    { "LOW",       "SAT",      0x02, true  }, // Pixel is low
+    { "SUSPECT",   NULL,       0x04, false }, // Pixel is suspected of being bad
+    // Non-astronomical structures
+    { "CR",        NULL,       0x08, true  }, // Pixel contains a cosmic ray
+    { "SPIKE",     NULL,       0x08, true  }, // Pixel contains a diffraction spike
+    { "GHOST",     NULL,       0x08, true  }, // Pixel contains an optical ghost
+    { "STREAK",    NULL,       0x08, true  }, // Pixel contains streak data
+    { "STARCORE",  NULL,       0x08, true  }, // Pixel contains a bright star core
+    // Effects of convolution and interpolation
+    { "CONV.BAD",  NULL,       0x02, true  }, // Pixel is bad after convolution with a bad pixel
+    { "CONV.POOR", NULL,       0x04, false }, // Pixel is poor after convolution with a bad pixel
 };
 
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/config/pmConfigRun.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/config/pmConfigRun.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/config/pmConfigRun.c	(revision 23594)
@@ -39,6 +39,9 @@
 }
 
-
-bool pmConfigRunFileAdd(pmConfig *config, const pmFPAfile *file)
+// Add a file to a nominated metadata in the RUN information
+static bool configRunFileAdd(pmConfig *config, // Configuration
+                             const pmFPAfile *file, // File to add
+                             const char *target // Name of metadata to which to add
+                             )
 {
     PS_ASSERT_PTR_NON_NULL(config, false);
@@ -47,5 +50,5 @@
     psMetadata *run = configRun(config);// RUN information
     psAssert(run, "Require run-time information");
-    psMetadata *files = configElement(run, "FILES", "Filerules used during execution");
+    psMetadata *files = configElement(run, target, "Filerules used during execution");
     psAssert(files, "Require list of files");
 
@@ -71,13 +74,35 @@
 }
 
-psArray *pmConfigRunFileGet(pmConfig *config, const char *name)
+bool pmConfigRunFileAddRead(pmConfig *config, const pmFPAfile *file)
 {
     PS_ASSERT_PTR_NON_NULL(config, false);
-    PS_ASSERT_STRING_NON_EMPTY(name, false);
+    PS_ASSERT_PTR_NON_NULL(file, false);
 
+    return configRunFileAdd(config, file, "FILES.INPUT");
+}
+
+bool pmConfigRunFileAddWrite(pmConfig *config, const pmFPAfile *file)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+    PS_ASSERT_PTR_NON_NULL(file, false);
+
+    return configRunFileAdd(config, file, "FILES.OUTPUT");
+}
+
+// Get an array of filenames from the nominated RUN information
+static psArray *configRunFileGet(pmConfig *config, // Configuration
+                                 const char *name, // Name of file
+                                 const char *source // Source metadata for file
+                                 )
+{
     psMetadata *run = configRun(config);// RUN information
     psAssert(run, "Require run-time information");
-    psMetadata *files = configElement(run, "FILES", "Filerules used during execution");
+    psMetadata *files = configElement(run, source, "Filerules used during execution");
     psAssert(files, "Require list of files");
+
+    if (psListLength(files->list) == 0) {
+        // Can't find anything
+        return NULL;
+    }
 
     psList *list = psListAlloc(NULL);   // List of file names
@@ -104,4 +129,19 @@
 
     return array;
+}
+
+
+psArray *pmConfigRunFileGet(pmConfig *config, const char *name)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+    PS_ASSERT_STRING_NON_EMPTY(name, false);
+
+    // Try the input and output, in turn
+    psArray *files = configRunFileGet(config, name, "FILES.INPUT"); // Files from RUN metadata
+    if (!files) {
+        configRunFileGet(config, name, "FILES.OUTPUT");
+    }
+
+    return files;
 }
 
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/config/pmConfigRun.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/config/pmConfigRun.h	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/config/pmConfigRun.h	(revision 23594)
@@ -6,6 +6,12 @@
 #include <pmFPAfile.h>
 
-/// Add a file to the list of files used in the run-time information
-bool pmConfigRunFileAdd(
+/// Add a file to the list of files read in the run-time information
+bool pmConfigRunFileAddRead(
+    pmConfig *config,                   ///< Configuration
+    const pmFPAfile *file               ///< File to add
+    );
+
+/// Add a file to the list of files written in the run-time information
+bool pmConfigRunFileAddWrite(
     pmConfig *config,                   ///< Configuration
     const pmFPAfile *file               ///< File to add
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/detrend/pmDetrendDB.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/detrend/pmDetrendDB.h	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/detrend/pmDetrendDB.h	(revision 23594)
@@ -63,5 +63,5 @@
 
 pmDetrendSelectOptions *pmDetrendSelectOptionsAlloc(const char *camera, psTime time, pmDetrendType type);
-pmDetrendSelectResults *pmDetrendSelectResultsAlloc();
+pmDetrendSelectResults *pmDetrendSelectResultsAlloc(void);
 pmDetrendSelectResults *pmDetrendSelect (const pmDetrendSelectOptions *options, const pmConfig *config);
 char *pmDetrendFile (const char *detID, const char *classID, const pmConfig *config);
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/detrend/pmDetrendThreads.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/detrend/pmDetrendThreads.h	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/detrend/pmDetrendThreads.h	(revision 23594)
@@ -18,5 +18,5 @@
 
 /// get the requested number of scan rows per thread
-int pmDetrendGetScanRows ();
+int pmDetrendGetScanRows(void);
 
 /// @}
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/detrend/pmShutterCorrection.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/detrend/pmShutterCorrection.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/detrend/pmShutterCorrection.c	(revision 23594)
@@ -71,5 +71,5 @@
 }
 
-pmShutterCorrection *pmShutterCorrectionAlloc()
+pmShutterCorrection *pmShutterCorrectionAlloc(void)
 {
     pmShutterCorrection *corr = (pmShutterCorrection*)psAlloc(sizeof(pmShutterCorrection));
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/detrend/pmShutterCorrection.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/detrend/pmShutterCorrection.h	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/detrend/pmShutterCorrection.h	(revision 23594)
@@ -69,5 +69,5 @@
 
 /// Allocator for shutter correction parameters
-pmShutterCorrection *pmShutterCorrectionAlloc();
+pmShutterCorrection *pmShutterCorrectionAlloc(void);
 
 /// Guess a shutter correction, based on plot of counts vs exposure time
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/extras/pmKapaPlots.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/extras/pmKapaPlots.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/extras/pmKapaPlots.c	(revision 23594)
@@ -47,5 +47,5 @@
 }
 
-bool pmKapaClose ()
+bool pmKapaClose (void)
 {
 
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/extras/pmKapaPlots.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/extras/pmKapaPlots.h	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/extras/pmKapaPlots.h	(revision 23594)
@@ -1,4 +1,4 @@
 /* @file  pmKapaPlots.h
- * @brief functions to make plots with the external program 'kapa' 
+ * @brief functions to make plots with the external program 'kapa'
  *
  * @author EAM, IfA
@@ -17,5 +17,5 @@
 // move to psLib or psModules
 int pmKapaOpen (bool showWindow);
-bool pmKapaClose ();
+bool pmKapaClose(void);
 bool pmKapaPlotVectorPair (psVector *xVec, psVector *yVec);
 
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/extras/psPipe.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/extras/psPipe.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/extras/psPipe.c	(revision 23594)
@@ -35,5 +35,5 @@
 }
 
-psPipe *psPipeAlloc ()
+psPipe *psPipeAlloc (void)
 {
     psPipe *pipe = (psPipe *)psAlloc(sizeof(psPipe));
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/extras/psPipe.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/extras/psPipe.h	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/extras/psPipe.h	(revision 23594)
@@ -1,4 +1,4 @@
 /* @file  psPipe.h
- * @brief 3-stream pipe 
+ * @brief 3-stream pipe
  *
  * @author EAM, IfA
@@ -26,5 +26,5 @@
 
 // psPipe functions
-psPipe *psPipeAlloc ();
+psPipe *psPipeAlloc (void);
 psPipe *psPipeOpen (char *command);
 int     psPipeClose (psPipe *pipe);
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/imcombine/pmStack.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/imcombine/pmStack.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/imcombine/pmStack.c	(revision 23594)
@@ -46,4 +46,5 @@
     psVector *weights;                  // Pixel weightings
     psVector *sources;                  // Pixel sources (which image did they come from?)
+    psVector *limits;                   // Rejection limits
     psVector *sort;                     // Buffer for sorting (to get a robust estimator of the standard dev)
 } combineBuffer;
@@ -56,4 +57,5 @@
     psFree(buffer->weights);
     psFree(buffer->sources);
+    psFree(buffer->limits);
     psFree(buffer->sort);
     return;
@@ -71,4 +73,5 @@
     buffer->weights = psVectorAlloc(numImages, PS_TYPE_F32);
     buffer->sources = psVectorAlloc(numImages, PS_TYPE_U16);
+    buffer->limits = psVectorAlloc(numImages, PS_TYPE_F32);
     buffer->sort = psVectorAlloc(numImages, PS_TYPE_F32);
     return buffer;
@@ -319,4 +322,5 @@
                           bool useVariance, // Use variance for rejection when combining?
                           bool safe,    // Combine safely?
+                          bool rejectInspect, // Reject values marked for inspection from combination?
                           combineBuffer *buffer // Buffer for combination; to avoid multiple allocations
                          )
@@ -336,4 +340,5 @@
     psVector *pixelWeights = buffer->weights; // Image weights for the pixel of interest
     psVector *pixelSources = buffer->sources; // Sources for the pixel of interest
+    psVector *pixelLimits = buffer->limits; // Limits for the pixel of interest
     psVector *sort = buffer->sort;      // Sort buffer
 
@@ -375,4 +380,5 @@
     pixelWeights->n = num;
     pixelSources->n = num;
+    pixelLimits->n = num;
 
 #ifdef TESTING
@@ -389,5 +395,5 @@
     // Default option is that the pixel is bad
     float imageValue = NAN, varianceValue = NAN; // Value for combined image and variance map
-    psImageMaskType maskValue = bad;         // Value for combined mask
+    psImageMaskType maskValue = bad;    // Value for combined mask
     switch (num) {
       case 0:
@@ -449,6 +455,12 @@
               if (PS_SQR(diff) > PS_SQR(rej) * sigma2) {
                   // Not consistent: mark both for inspection
-                  combineInspect(inputs, x, y, pixelSources->data.U16[0]);
-                  combineInspect(inputs, x, y, pixelSources->data.U16[1]);
+                  if (rejectInspect) {
+                      imageValue = NAN;
+                      varianceValue = NAN;
+                      maskValue = bad;
+                  } else {
+                      combineInspect(inputs, x, y, pixelSources->data.U16[0]);
+                      combineInspect(inputs, x, y, pixelSources->data.U16[1]);
+                  }
 #ifdef TESTING
                   if (x == TEST_X && y == TEST_Y) {
@@ -501,5 +513,5 @@
 #endif
 
-                      pixelVariances->data.F32[i] = rej2 * (pixelVariances->data.F32[i] + sysVar);
+                      pixelLimits->data.F32[i] = rej2 * (pixelVariances->data.F32[i] + sysVar);
                   }
               }
@@ -541,5 +553,7 @@
 #define MASK_PIXEL_FOR_INSPECTION() \
     pixelMasks->data.PS_TYPE_VECTOR_MASK_DATA[j] = 0xff; \
-    combineInspect(inputs, x, y, pixelSources->data.U16[j]); \
+    if (!rejectInspect) { \
+        combineInspect(inputs, x, y, pixelSources->data.U16[j]); \
+    } \
     numClipped++; \
     totalClipped++;
@@ -553,10 +567,10 @@
                       // Comparing squares --- cheaper than lots of sqrts
                       // pixelVariances includes the rejection limit, from above
-                      if (PS_SQR(diff) > pixelVariances->data.F32[j]) {
+                      if (PS_SQR(diff) > pixelLimits->data.F32[j]) {
                           MASK_PIXEL_FOR_INSPECTION();
 #ifdef TESTING
                           if (x == TEST_X && y == TEST_Y) {
                               fprintf(stderr, "Rejecting input %d based on variance: %f > %f\n",
-                                      j, diff, sqrtf(pixelVariances->data.F32[j]));
+                                      j, diff, sqrtf(pixelLimits->data.F32[j]));
                           }
 #endif
@@ -573,4 +587,35 @@
               }
           }
+
+          if (rejectInspect && totalClipped > 0) {
+              // Get rid of the masked values
+              // The alternative to this is to make combinationMeanVariance() accept a mask
+              int good = 0;            // Index of good value
+              for (int i = 0; i < num; i++) {
+                  if (pixelMasks->data.PS_TYPE_VECTOR_MASK_DATA[i]) {
+                      continue;
+                  }
+                  if (i != good) {
+                      pixelData->data.F32[good] = pixelData->data.F32[i];
+                      pixelVariances->data.F32[good] = pixelVariances->data.F32[i];
+                      pixelWeights->data.F32[good] = pixelWeights->data.F32[i];
+                      pixelData->data.F32[good] = pixelData->data.F32[i];
+                  }
+                  good++;
+              }
+              pixelData->n = good;
+              pixelVariances->n = good;
+              pixelWeights->n = good;
+              if (combinationMeanVariance(&mean, &var, pixelData, pixelVariances, pixelWeights)) {
+                  imageValue = mean;
+                  varianceValue = var;
+                  maskValue = 0;
+              } else {
+                  imageValue = NAN;
+                  varianceValue = NAN;
+                  maskValue = bad;
+              }
+          }
+
           break;
       }
@@ -734,5 +779,5 @@
 bool pmStackCombine(pmReadout *combined, psArray *input, psImageMaskType maskVal, psImageMaskType bad,
                     int kernelSize, int numIter, float rej, float sys, float discard,
-                    bool entire, bool useVariance, bool safe)
+                    bool entire, bool useVariance, bool safe, bool rejectInspect)
 {
     PS_ASSERT_PTR_NON_NULL(combined, false);
@@ -838,5 +883,5 @@
                     combinePixels(combinedImage, combinedMask, combinedVariance, input, weights,
                                   addVariance, reject, x, y, maskVal, bad, numIter, rej, sys, discard,
-                                  useVariance, safe, buffer);
+                                  useVariance, safe, rejectInspect, buffer);
                 }
             }
@@ -853,5 +898,5 @@
                 combinePixels(combinedImage, combinedMask, combinedVariance, input, weights,
                               addVariance, reject, x, y, maskVal, bad, numIter, rej, sys, discard,
-                              useVariance, safe, buffer);
+                              useVariance, safe, rejectInspect, buffer);
             }
         }
@@ -881,5 +926,5 @@
                 combinePixels(combinedImage, combinedMask, combinedVariance, input, weights,
                               addVariance, NULL, x, y, maskVal, bad, numIter, rej, sys, discard,
-                              useVariance, safe, buffer);
+                              useVariance, safe, rejectInspect, buffer);
             }
         }
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/imcombine/pmStack.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/imcombine/pmStack.h	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/imcombine/pmStack.h	(revision 23594)
@@ -52,5 +52,6 @@
                     bool entire,        ///< Combine entire image even if rejection lists provided?
                     bool useVariance,   ///< Use variance values for rejection?
-                    bool safe           ///< Play safe with small numbers of input pixels (mask if N <= 2)?
+                    bool safe,          ///< Play safe with small numbers of input pixels (mask if N <= 2)?
+                    bool rejectInspect  ///< Reject pixels instead of marking them for inspection?
     );
 
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/imcombine/pmSubtractionIO.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/imcombine/pmSubtractionIO.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/imcombine/pmSubtractionIO.c	(revision 23594)
@@ -4,4 +4,5 @@
 
 #include "pmHDU.h"
+#include "pmHDUUtils.h"
 #include "pmFPA.h"
 #include "pmFPAview.h"
@@ -143,4 +144,14 @@
     psMetadata *header = psMetadataAlloc(); // Header for FITS file
 
+    pmCell *cell = ro->parent;          // Cell of interest
+    if (cell) {
+        pmChip *chip = cell->parent;    // Chip of interest
+        pmFPA *fpa = chip->parent;      // FPA of interest
+        pmHDU *hdu = pmHDUGetHighest(fpa, chip, cell); // HDU for readout
+        if (hdu) {
+            header = psMetadataCopy(header, hdu->header);
+        }
+    }
+
     // CVS tags, used to identify the version of this file (in case incompatibilities are introduced)
     psString cvsFile = psStringCopy("$RCSfile: pmSubtractionIO.c,v $");
@@ -533,10 +544,4 @@
         return true;
     }
-    if (file->fileLevel != PM_FPA_LEVEL_FPA) {
-        return true;
-    }
-    if (file->fpa->chips->n == 1) {
-        return true;
-    }
 
     // find the FPA phu
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/imcombine/pmSubtractionVisual.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/imcombine/pmSubtractionVisual.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/imcombine/pmSubtractionVisual.c	(revision 23594)
@@ -44,5 +44,5 @@
 
 /** destroy windows at the end of a run*/
-bool pmSubtractionVisualClose()
+bool pmSubtractionVisualClose(void)
 {
     if(kapa != -1)
@@ -256,5 +256,5 @@
 
 #else
-bool pmSubtractionVisualClose() {return true;}
+bool pmSubtractionVisualClose(void) {return true;}
 bool pmSubtractionVisualPlotConvKernels(psImage *convKernels) {return true;}
 bool pmSubtractionVisualPlotStamps(pmSubtractionStampList *stamps, pmReadout *ro) {return true;}
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/imcombine/pmSubtractionVisual.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/imcombine/pmSubtractionVisual.h	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/imcombine/pmSubtractionVisual.h	(revision 23594)
@@ -2,5 +2,5 @@
 #define PM_SUBTRACTION_VISUAL_H
 
-bool pmSubtractionVisualClose();
+bool pmSubtractionVisualClose(void);
 bool pmSubtractionVisualPlotConvKernels(psImage *convKernels);
 bool pmSubtractionVisualPlotStamps(pmSubtractionStampList *stamps, pmReadout *ro);
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmDetections.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmDetections.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmDetections.c	(revision 23594)
@@ -30,5 +30,5 @@
 
 // generate a pmDetections container with empty (allocated) footprints and peaks containers
-pmDetections *pmDetectionsAlloc() {
+pmDetections *pmDetectionsAlloc(void) {
 
     pmDetections *detections = (pmDetections *)psAlloc(sizeof(pmDetections));
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmDetections.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmDetections.h	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmDetections.h	(revision 23594)
@@ -20,11 +20,11 @@
  */
 typedef struct {
-  psArray *footprints;	      // collection of footprints in the image
-  psArray *peaks;	      // collection of all peaks contained by the footprints
-  psArray *oldPeaks;	      // collection of all peaks previously found
+  psArray *footprints;        // collection of footprints in the image
+  psArray *peaks;             // collection of all peaks contained by the footprints
+  psArray *oldPeaks;          // collection of all peaks previously found
   int last;
 } pmDetections;
 
-pmDetections *pmDetectionsAlloc ();
+pmDetections *pmDetectionsAlloc (void);
 
 /// @}
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmMoments.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmMoments.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmMoments.c	(revision 23594)
@@ -25,5 +25,5 @@
 to zero.
 *****************************************************************************/
-pmMoments *pmMomentsAlloc()
+pmMoments *pmMomentsAlloc(void)
 {
     psTrace("psModules.objects", 10, "---- %s() begin ----\n", __func__);
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmMoments.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmMoments.h	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmMoments.h	(revision 23594)
@@ -53,5 +53,5 @@
  *
  */
-pmMoments *pmMomentsAlloc();
+pmMoments *pmMomentsAlloc(void);
 
 /// @}
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmPSF.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmPSF.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmPSF.c	(revision 23594)
@@ -54,5 +54,5 @@
 }
 
-pmPSFOptions *pmPSFOptionsAlloc () {
+pmPSFOptions *pmPSFOptionsAlloc (void) {
 
     pmPSFOptions *options = (pmPSFOptions *) psAlloc(sizeof(pmPSFOptions));
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmPSF.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmPSF.h	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmPSF.h	(revision 23594)
@@ -96,5 +96,5 @@
 pmPSF *pmPSFAlloc (const pmPSFOptions *options);
 bool psMemCheckPSF(psPtr ptr);
-pmPSFOptions *pmPSFOptionsAlloc();
+pmPSFOptions *pmPSFOptionsAlloc(void);
 bool psMemCheckPSFOptions(psPtr ptr);
 
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmSource.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmSource.h	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmSource.h	(revision 23594)
@@ -105,5 +105,5 @@
  *
  */
-pmSource  *pmSourceAlloc();
+pmSource  *pmSourceAlloc(void);
 
 /** pmSourceCopy()
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmSourceExtendedPars.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmSourceExtendedPars.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmSourceExtendedPars.c	(revision 23594)
@@ -46,5 +46,5 @@
 }
 
-pmSourceExtendedPars *pmSourceExtendedParsAlloc () {
+pmSourceExtendedPars *pmSourceExtendedParsAlloc (void) {
     pmSourceExtendedPars *pars = (pmSourceExtendedPars *) psAlloc(sizeof(pmSourceExtendedPars));
     psMemSetDeallocator(pars, (psFreeFunc) pmSourceExtendedParsFree);
@@ -75,5 +75,5 @@
 }
 
-pmSourceRadialProfile *pmSourceRadialProfileAlloc () {
+pmSourceRadialProfile *pmSourceRadialProfileAlloc (void) {
 
     pmSourceRadialProfile *profile = (pmSourceRadialProfile *) psAlloc(sizeof(pmSourceRadialProfile));
@@ -99,5 +99,5 @@
 }
 
-pmSourceIsophotalValues *pmSourceIsophotalValuesAlloc () {
+pmSourceIsophotalValues *pmSourceIsophotalValuesAlloc (void) {
 
     pmSourceIsophotalValues *isophot = (pmSourceIsophotalValues *) psAlloc(sizeof(pmSourceIsophotalValues));
@@ -125,5 +125,5 @@
 }
 
-pmSourcePetrosianValues *pmSourcePetrosianValuesAlloc () {
+pmSourcePetrosianValues *pmSourcePetrosianValuesAlloc (void) {
 
     pmSourcePetrosianValues *petrosian = (pmSourcePetrosianValues *) psAlloc(sizeof(pmSourcePetrosianValues));
@@ -150,5 +150,5 @@
 }
 
-pmSourceKronValues *pmSourceKronValuesAlloc () {
+pmSourceKronValues *pmSourceKronValuesAlloc (void) {
 
     pmSourceKronValues *kron = (pmSourceKronValues *) psAlloc(sizeof(pmSourceKronValues));
@@ -181,5 +181,5 @@
 }
 
-pmSourceAnnuli *pmSourceAnnuliAlloc () {
+pmSourceAnnuli *pmSourceAnnuliAlloc (void) {
 
     pmSourceAnnuli *annuli = (pmSourceAnnuli *) psAlloc(sizeof(pmSourceAnnuli));
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmSourceExtendedPars.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmSourceExtendedPars.h	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmSourceExtendedPars.h	(revision 23594)
@@ -55,15 +55,15 @@
 } pmSourceExtendedPars;
 
-pmSourceExtendedPars *pmSourceExtendedParsAlloc ();
+pmSourceExtendedPars *pmSourceExtendedParsAlloc(void);
 bool psMemCheckSourceExtendedPars(psPtr ptr);
-pmSourceRadialProfile *pmSourceRadialProfileAlloc ();
+pmSourceRadialProfile *pmSourceRadialProfileAlloc(void);
 bool psMemCheckSourceRadialProfile(psPtr ptr);
-pmSourceIsophotalValues *pmSourceIsophotalValuesAlloc ();
+pmSourceIsophotalValues *pmSourceIsophotalValuesAlloc(void);
 bool psMemCheckSourceIsophotalValues(psPtr ptr);
-pmSourcePetrosianValues *pmSourcePetrosianValuesAlloc ();
+pmSourcePetrosianValues *pmSourcePetrosianValuesAlloc(void);
 bool psMemCheckSourcePetrosianValues(psPtr ptr);
-pmSourceKronValues *pmSourceKronValuesAlloc ();
+pmSourceKronValues *pmSourceKronValuesAlloc(void);
 bool psMemCheckSourceKronValues(psPtr ptr);
-pmSourceAnnuli *pmSourceAnnuliAlloc ();
+pmSourceAnnuli *pmSourceAnnuliAlloc(void);
 bool psMemCheckSourceAnnuli(psPtr ptr);
 
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmSourceFitSet.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmSourceFitSet.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmSourceFitSet.c	(revision 23594)
@@ -67,5 +67,5 @@
 }
 
-void pmSourceFitSetDone () {
+void pmSourceFitSetDone (void) {
     psFree (fitSets);
 }
@@ -150,5 +150,5 @@
 }
 
-pmSourceFitSetData *pmSourceFitSetDataGet () {
+pmSourceFitSetData *pmSourceFitSetDataGet (void) {
 
     psAssert (fitSets, "pmSourceFitSetInit not called");
@@ -172,5 +172,5 @@
 }
 
-void pmSourceFitSetDataClear () {
+void pmSourceFitSetDataClear (void) {
 
     int i;
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmSourceFitSet.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmSourceFitSet.h	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmSourceFitSet.h	(revision 23594)
@@ -24,5 +24,5 @@
 // use this function to init the fit sets based on the number of threads
 bool pmSourceFitSetInit (int nThreads);
-void pmSourceFitSetDone ();
+void pmSourceFitSetDone (void);
 
 // initialize data for a group of object models
@@ -32,6 +32,6 @@
 // functions for selecting the FitSet corresponding to the current thread
 pmSourceFitSetData *pmSourceFitSetDataSet (psArray *modelSet);
-pmSourceFitSetData *pmSourceFitSetDataGet ();
-void pmSourceFitSetDataClear ();
+pmSourceFitSetData *pmSourceFitSetDataGet (void);
+void pmSourceFitSetDataClear (void);
 
 // function used to set limits for a group of models
@@ -54,8 +54,8 @@
  */
 bool pmSourceFitSet(
-    pmSource *source,			///< The input pmSource
-    psArray *modelSet,			///< model to be fitted
-    pmSourceFitMode mode,		///< define parameters to be fitted
-    psImageMaskType maskVal		///< Vale to mask
+    pmSource *source,                   ///< The input pmSource
+    psArray *modelSet,                  ///< model to be fitted
+    pmSourceFitMode mode,               ///< define parameters to be fitted
+    psImageMaskType maskVal             ///< Vale to mask
 
 );
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmSourcePlots.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmSourcePlots.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmSourcePlots.c	(revision 23594)
@@ -139,5 +139,5 @@
 }
 
-pmSourcePlotLayout *pmSourcePlotLayoutAlloc()
+pmSourcePlotLayout *pmSourcePlotLayoutAlloc(void)
 {
     pmSourcePlotLayout *layout = (pmSourcePlotLayout *)psAlloc(sizeof(pmSourcePlotLayout));
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmSourcePlots.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmSourcePlots.h	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmSourcePlots.h	(revision 23594)
@@ -29,5 +29,5 @@
 // typedef bool (*pmSourcePlotFunction)(pmConfig *config, pmFPAview *view, pmSourcePlotLayout *layout);
 
-pmSourcePlotLayout *pmSourcePlotLayoutAlloc();
+pmSourcePlotLayout *pmSourcePlotLayoutAlloc(void);
 bool psMemCheckSourcePlotLayout(psPtr ptr);
 
Index: /branches/cnb_branches/cnb_branch_20090301/psastro/src/Makefile.am
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psastro/src/Makefile.am	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/psastro/src/Makefile.am	(revision 23594)
@@ -10,9 +10,13 @@
 # FORCE: ;
 
-bin_PROGRAMS = psastro psastroModel psastroModelFit gpcModel
+bin_PROGRAMS = psastro psastroExtract psastroModel psastroModelFit gpcModel
 
 psastro_CFLAGS = $(PSASTRO_CFLAGS) $(PPSTATS_CFLAGS) $(PSMODULE_CFLAGS) $(PSLIB_CFLAGS)
 psastro_LDFLAGS = $(PSASTRO_LIBS) $(PPSTATS_LIBS) $(PSMODULE_LIBS) $(PSLIB_LIBS)
 psastro_LDADD = libpsastro.la
+
+psastroExtract_CFLAGS = $(PSASTRO_CFLAGS) $(PSMODULE_CFLAGS) $(PSLIB_CFLAGS)
+psastroExtract_LDFLAGS = $(PSASTRO_LIBS) $(PSMODULE_LIBS) $(PSLIB_LIBS)
+psastroExtract_LDADD = libpsastro.la
 
 psastroModel_CFLAGS = $(PSASTRO_CFLAGS) $(PSMODULE_CFLAGS) $(PSLIB_CFLAGS)
@@ -35,4 +39,16 @@
 	psastroDataSave.c           \
 	psastroMetadataStats.c      \
+	psastroCleanup.c
+
+psastroExtract_SOURCES = \
+	psastroExtract.c		    \
+        psastroExtractArguments.c	    \
+	psastroExtractParseCamera.c   \
+	psastroExtractDataLoad.c      \
+	psastroExtractAnalysis.c      \
+	psastroExtractStar.c      \
+	psastroExtractStars.c      \
+	psastroExtractGhosts.c      \
+	psastroExtractFindChip.c      \
 	psastroCleanup.c
 
Index: /branches/cnb_branches/cnb_branch_20090301/psastro/src/psastro.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psastro/src/psastro.h	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/psastro/src/psastro.h	(revision 23594)
@@ -58,13 +58,10 @@
 bool              psastroMaskUpdates (pmConfig *config);
 
-psPlaneDistort   *psPlaneDistortIdentity ();
-bool              psPlaneDistortIsIdentity (psPlaneDistort *distort);
-
-psArray          *psastroLoadRefstars (pmConfig *config);
+psArray          *psastroLoadRefstars (pmConfig *config, const char *source);
 bool              psastroChipAstrom (pmConfig *config);
 bool              psastroOneChip (pmFPA *fpa, pmChip *chip, psArray *refset, psArray *rawset, psMetadata *recipe, psMetadata *updates);
 bool              psastroOneChipGrid (pmFPA *fpa, pmChip *chip, psArray *refset, psArray *rawset, psMetadata *recipe, psMetadata *updates);
 bool              psastroOneChipFit (pmFPA *fpa, pmChip *chip, psArray *refset, psArray *rawset, psMetadata *recipe, psMetadata *updates);
-bool              psastroChooseRefstars (pmConfig *config, psArray *refs);
+bool              psastroChooseRefstars (pmConfig *config, psArray *refs, const char *source);
 bool              psastroRefstarSubset (pmReadout *readout);
 pmLumFunc        *psastroLuminosityFunction (psArray *stars, pmLumFunc *rawFunc);
@@ -121,4 +118,5 @@
 bool              psastroDumpCorners (char *filenameU, char *filenameD, pmFPA *fpa);
 
+char             *psastroSetMagLimit (float *minMag, float *maxRho, pmConfig *config, const char *source);
 
 psArray          *psastroReadGetstarCatalog (psFits *fits);
@@ -129,7 +127,19 @@
 bool              psastroMetadataStats (pmConfig *config);
 
-bool psastroZeroPoint (pmConfig *config);
-bool psastroZeroPointReadout(pmReadout *readout, float zeropt, float exptime);
-bool psastroZeroPointFromRecipe (float *zeropt, float *exptime, pmFPA *fpa, psMetadata *recipe);
+bool 		  psastroZeroPoint (pmConfig *config);
+bool 		  psastroZeroPointReadout(pmReadout *readout, float zeropt, float exptime);
+bool 		  psastroZeroPointFromRecipe (float *zeropt, float *exptime, pmFPA *fpa, psMetadata *recipe);
+
+// psastroExtract functions
+bool 		  psastroExtractAnalysis (pmConfig *config);
+bool 		  psastroExtractStars (pmConfig *config);
+bool 		  psastroExtractGhosts (pmConfig *config);
+pmChip           *psastroExtractFindChip (float *xChip, float *yChip, pmFPA *fpa, float xFPA, float yFPA);
+bool 		  psastroExtractChipBounds (pmFPA *fpa);
+
+psImage          *psastroExtractStar (psImage *input, float x, float y, float dX, float dY);
+bool 		  psastroExtractParseCamera (pmConfig *config);
+bool 		  psastroExtractDataLoad (pmConfig *config);
+pmConfig         *psastroExtractArguments (int argc, char **argv);
 
 ///@}
Index: /branches/cnb_branches/cnb_branch_20090301/psastro/src/psastroAnalysis.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psastro/src/psastroAnalysis.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/psastro/src/psastroAnalysis.c	(revision 23594)
@@ -45,5 +45,5 @@
 
     // load the reference stars overlapping the data stars
-    psArray *refs = psastroLoadRefstars(config);
+    psArray *refs = psastroLoadRefstars(config, "PSASTRO.INPUT");
     if (!refs) {
         psError (PSASTRO_ERR_UNKNOWN, false, "failed to load reference data\n");
@@ -56,9 +56,10 @@
     }
 
-    if (!psastroChooseRefstars (config, refs)) {
+    if (!psastroChooseRefstars (config, refs, "PSASTRO.INPUT")) {
         psError (PSASTRO_ERR_UNKNOWN, false, "failed to select reference data for chips\n");
         psFree(refs);
         return false;
     }
+    psFree (refs);  // refs of interest are saved on readout->analysis
 
     // check the command-line arguments first
@@ -79,5 +80,4 @@
         if (!psastroChipAstrom (config)) {
             psError (PSASTRO_ERR_UNKNOWN, false, "failed to perform single chip astrometry\n");
-            psFree(refs);
             return false;
         }
@@ -86,5 +86,4 @@
         if (!psastroMosaicAstrom (config)) {
             psError (PSASTRO_ERR_UNKNOWN, false, "failed to perform mosaic camera astrometry\n");
-            psFree(refs);
             return false;
         }
@@ -100,5 +99,4 @@
     // psastroStackAstrom (config, refs);
 
-    psFree (refs);
     return true;
 }
Index: /branches/cnb_branches/cnb_branch_20090301/psastro/src/psastroChooseRefstars.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psastro/src/psastroChooseRefstars.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/psastro/src/psastroChooseRefstars.c	(revision 23594)
@@ -20,5 +20,5 @@
 }
 
-bool psastroChooseRefstars (pmConfig *config, psArray *refs) {
+bool psastroChooseRefstars (pmConfig *config, psArray *refs, const char *source) {
 
     bool status;
@@ -35,5 +35,5 @@
 
     // select the input data sources
-    pmFPAfile *input = psMetadataLookupPtr (NULL, config->files, "PSASTRO.INPUT");
+    pmFPAfile *input = psMetadataLookupPtr (NULL, config->files, source);
     if (!input) {
         psError(PSASTRO_ERR_CONFIG, true, "Can't find input data!\n");
Index: /branches/cnb_branches/cnb_branch_20090301/psastro/src/psastroExtract.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psastro/src/psastroExtract.c	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/psastro/src/psastroExtract.c	(revision 23594)
@@ -0,0 +1,59 @@
+/** @file psastroExtract.c
+ *
+ *  @brief
+ *
+ *  @ingroup psastroExtract
+ *
+ *  @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
+ */
+
+# include "psastroStandAlone.h"
+
+int main (int argc, char **argv) {
+
+    pmConfig *config = NULL;
+
+    psTimerStart ("complete");
+
+    psastroErrorRegister();              // register our error codes/messages
+
+    // model inits are needed in pmSourceIO
+    // models defined in psphot/src/models are not available in psastro
+    pmModelClassInit ();
+
+    // load configuration information
+    config = psastroExtractArguments (argc, argv);
+
+    // load identify the data sources
+    if (!psastroExtractParseCamera (config)) {
+	psErrorStackPrint(stderr, "error setting up the camera\n");
+	exit (1);
+    }
+
+    // load the raw pixel data (from PSPHOT.SOURCES)
+    // select subset of stars for astrometry
+    if (!psastroExtractDataLoad (config)) {
+	psErrorStackPrint(stderr, "error loading input data\n");
+	exit (1);
+    }
+
+    // run the full astrometry analysis (chip and/or mosaic)
+    if (!psastroExtractAnalysis (config)) {
+	psErrorStackPrint(stderr, "failure in psastro model analysis\n");
+	exit (1);
+    }
+    
+    // XXX save the results; is this needed?
+    // if (!psastroExtractDataSave (config)) {
+    // psErrorStackPrint(stderr, "error saving output data\n");
+    // exit (1);
+    // }
+
+    psLogMsg ("psastro", 3, "complete psastro run: %f sec\n", psTimerMark ("complete"));
+
+    psastroCleanup (config);
+    exit (EXIT_SUCCESS);
+}
Index: /branches/cnb_branches/cnb_branch_20090301/psastro/src/psastroExtractAnalysis.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psastro/src/psastroExtractAnalysis.c	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/psastro/src/psastroExtractAnalysis.c	(revision 23594)
@@ -0,0 +1,58 @@
+/** @file psastroExtractAnalysis.c
+ *
+ *  @brief 
+ *
+ *  @ingroup psastroExtract
+ *
+ *  @author IfA
+ *  @version $Revision: 1.7 $
+ *  @date $Date: 2009-02-07 02:03:34 $
+ *  Copyright 2009 Institute for Astronomy, University of Hawaii
+ */
+
+# include "psastroInternal.h"
+
+/**
+ * create a mask or mask regions based on the collection of reference stars that * are in the vicinity of each chip
+ */
+bool psastroExtractAnalysis (pmConfig *config) {
+
+    bool status;
+
+    // select the current recipe
+    psMetadata *recipe  = psMetadataLookupPtr (&status, config->recipes, PSASTRO_RECIPE);
+    if (!recipe) {
+        psError(PSASTRO_ERR_CONFIG, true, "Can't find PSASTRO recipe");
+        return false;
+    }
+
+    // force this recipe value to be false (would cause an error in psastroChooseRefstars -- no rawstars!)
+    psMetadataAddBool (recipe, PS_LIST_TAIL, "PSASTRO.MATCH.LUMFUNC", PS_META_REPLACE, "do not match luminosity functions", false);
+
+    psLogMsg ("psastro", PS_LOG_INFO, "loading reference stars");
+
+    // load the reference stars overlapping the data stars
+    psArray *refs = psastroLoadRefstars(config, "PSASTRO.EXTRACT.ASTROM");
+    if (!refs) {
+        psError (PSASTRO_ERR_UNKNOWN, false, "failed to load reference data\n");
+        return false;
+    }
+    if (refs->n == 0) {
+        psError(PSASTRO_ERR_REFSTARS, true, "no reference stars found");
+        psFree(refs);
+        return false;
+    }
+
+    if (!psastroChooseRefstars (config, refs, "PSASTRO.EXTRACT.ASTROM")) {
+        psError (PSASTRO_ERR_UNKNOWN, false, "failed to select reference data for chips\n");
+        psFree(refs);
+        return false;
+    }
+    psFree(refs);
+
+    // convert star positions to ghost positions and add to the readout->analysis data
+    psastroExtractGhosts (config);
+
+    psastroExtractStars (config);
+    return true;
+}
Index: /branches/cnb_branches/cnb_branch_20090301/psastro/src/psastroExtractArguments.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psastro/src/psastroExtractArguments.c	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/psastro/src/psastroExtractArguments.c	(revision 23594)
@@ -0,0 +1,78 @@
+/** @file psastroExtractArguments.c
+ *
+ *  @brief
+ *
+ *  @ingroup psastroExtract
+ *
+ *  @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
+ */
+
+# include "psastroStandAlone.h"
+
+static char *usage = "USAGE: psastroExtract -file (input) -astrom (cmffiles) -outroot (outroot) [-chip]";
+
+pmConfig *psastroExtractArguments (int argc, char **argv) {
+
+    int N;
+
+    if (argc == 1) {
+        psError(PSASTRO_ERR_ARGUMENTS, true, "No arguments supplied");
+        psErrorStackPrint(stderr, "%s", usage);
+	exit (1);
+    }
+
+    // load config data from default locations
+    pmConfig *config = pmConfigRead(&argc, argv, PSASTRO_RECIPE);
+    if (config == NULL) {
+        psError(PSASTRO_ERR_CONFIG, false, "Can't read site configuration");
+        psErrorStackPrint(stderr, "%s", usage);
+	exit (1);
+    }
+
+    // save the following additional recipe values based on command-line options
+    // these options override the PSASTRO recipe values loaded from recipe files
+    // psMetadata *options = pmConfigRecipeOptions (config, PSASTRO_RECIPE);
+
+    // define the image pixel data
+    if (!pmConfigFileSetsMD (config->arguments, &argc, argv, "INPUT", "-file", "-list")) {
+        psError(PSASTRO_ERR_ARGUMENTS, true, "Missing -file (input) or -list (input)");
+        psErrorStackPrint(stderr, "%s", usage);
+	exit (1);
+    }
+
+    // define the astrometry data (XXX make this optional and use the image header?)
+    if (!pmConfigFileSetsMD(config->arguments, &argc, argv, "ASTROM",   "-astrom", "-astromlist")) {
+        psError(PSASTRO_ERR_ARGUMENTS, true, "Missing -astrom (smf) or -astromlist (smf)");
+        psErrorStackPrint(stderr, "%s", usage);
+	exit (1);
+    }
+
+    // define the output filename
+    N = psArgumentGet (argc, argv, "-output");
+    if (!N) {
+	psError(PSASTRO_ERR_ARGUMENTS, true, "Missing -output (root)");
+        psErrorStackPrint(stderr, "%s", usage);
+	exit (1);
+    }
+    psArgumentRemove (N, &argc, argv);
+    psMetadataAddStr (config->arguments, PS_LIST_TAIL, "OUTPUT", 0, "", argv[N]);
+    psArgumentRemove (N, &argc, argv);
+
+    // chip selection is used to limit chips to be processed
+    if ((N = psArgumentGet (argc, argv, "-chip"))) {
+        psArgumentRemove (N, &argc, argv);
+        psMetadataAddStr (config->arguments, PS_LIST_TAIL, "CHIP_SELECTIONS", PS_META_REPLACE, "", argv[N]);
+        psArgumentRemove (N, &argc, argv);
+    }
+    
+    if (argc < 1) {
+        psError(PSASTRO_ERR_ARGUMENTS, true, "Incorrect arguments supplied");
+        psErrorStackPrint(stderr, "exit");
+	exit (1);
+    }
+
+    return (config);
+}
Index: /branches/cnb_branches/cnb_branch_20090301/psastro/src/psastroExtractDataLoad.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psastro/src/psastroExtractDataLoad.c	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/psastro/src/psastroExtractDataLoad.c	(revision 23594)
@@ -0,0 +1,165 @@
+/** @file psastroExtractDataLoad.c
+ *
+ *  @brief
+ *
+ *  @ingroup psastroExtract
+ *
+ *  @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
+ */
+
+# include "psastroStandAlone.h"
+
+# define ESCAPE { \
+  psError(PS_ERR_UNKNOWN, false, "Failure in psastroExtractDataLoad"); \
+  psFree (view); \
+  return false; \
+}
+  
+/**
+ * this loop loads the header data from the input files, using the output 
+ * pmFPAfile to guide the chip selection and related issues
+ * all of the different astrometry analysis modes use the same data load loop
+ */
+bool psastroExtractDataLoad (pmConfig *config) {
+
+    bool status;
+    pmChip *chip;
+
+    bool newFPA = true;
+    double RAmin  = +FLT_MAX;
+    double RAmax  = -FLT_MAX;
+    double DECmin = +FLT_MAX;
+    double DECmax = -FLT_MAX;
+
+    double RAminSky = NAN;
+    double RAmaxSky = NAN;
+
+    psTimerStart ("psastro");
+
+    // select the current recipe
+    psMetadata *recipe  = psMetadataLookupPtr (NULL, config->recipes, PSASTRO_RECIPE);
+    if (!recipe) {
+	psError(PSASTRO_ERR_CONFIG, true, "Can't find PSASTRO recipe!\n");
+	return false;
+    }
+
+    // physical pixel scale in microns per pixel
+    double pixelScale = psMetadataLookupF32 (&status, recipe, "PSASTRO.PIXEL.SCALE");
+    if (!status) {
+	psError(PS_ERR_IO, true, "Failed to lookup pixel scale"); 
+	return false; 
+    } 
+
+    // select the input data sources
+    pmFPAfile *astrom = psMetadataLookupPtr (NULL, config->files, "PSASTRO.EXTRACT.ASTROM");
+    if (!astrom) psAbort ("PSASTRO.EXTRACT.ASTROM not listed in config->files");
+    pmFPA *fpa = astrom->fpa;
+
+    pmFPAfileActivate(config->files, false, NULL);
+    pmFPAfileActivate(config->files, true, "PSASTRO.EXTRACT.ASTROM");
+
+    pmFPAview *view = pmFPAviewAlloc (0);
+
+    // files associated with the science image
+    if (!pmFPAfileIOChecks (config, view, PM_FPA_BEFORE)) ESCAPE;
+
+    // load the headers from the astrometry files
+    while ((chip = pmFPAviewNextChip (view, astrom->fpa, 1)) != NULL) {
+        psTrace ("psastro", 4, "Chip %d: %x %x\n", view->chip, chip->file_exists, chip->process);
+        if (!chip->process) { continue; }
+	if (!pmFPAfileIOChecks (config, view, PM_FPA_BEFORE)) ESCAPE;
+    }
+    psLogMsg ("psastro", 3, "load headers : %f sec\n", psTimerMark ("psastro"));
+
+    // check PHU header to see if we are using mosaic-level or per-chip astrometry
+    bool bilevelAstrometry = false;
+    pmHDU *phu = pmFPAviewThisPHU (view, astrom->fpa);
+    if (phu) {
+      char *ctype = psMetadataLookupStr (NULL, phu->header, "CTYPE1");
+      if (ctype) bilevelAstrometry = !strcmp (&ctype[4], "-DIS");
+    }
+    if (bilevelAstrometry) {
+      pmAstromReadBilevelMosaic (astrom->fpa, phu->header);
+    } 
+
+    // apply the header astrometry to the astrometry structures
+    while ((chip = pmFPAviewNextChip (view, astrom->fpa, 1)) != NULL) {
+      psTrace ("psastro", 4, "Chip %d: %x %x\n", view->chip, chip->file_exists, chip->process);
+      if (!chip->process || !chip->file_exists || !chip->data_exists) { continue; }
+
+      if (newFPA) {
+	newFPA = false;
+	while (fpa->toSky->R <        0) fpa->toSky->R += 2.0*M_PI;
+	while (fpa->toSky->R > 2.0*M_PI) fpa->toSky->R -= 2.0*M_PI;
+	RAminSky = fpa->toSky->R - M_PI;
+	RAmaxSky = fpa->toSky->R + M_PI;
+      }
+
+      // read WCS data from the corresponding header
+      pmHDU *hdu = pmFPAviewThisHDU (view, astrom->fpa);
+      int nAstro = psMetadataLookupS32 (&status, hdu->header, "NASTRO");
+      if (!nAstro) continue;
+
+      if (bilevelAstrometry) {
+	if (!pmAstromReadBilevelChip (chip, hdu->header)) {
+	  psWarning("Could not get WCS information from header for chip %d, skipping", view->chip); 
+	  continue;
+	} 
+      } else {
+	if (!pmAstromReadWCS (astrom->fpa, chip, hdu->header, pixelScale)) {
+	  psWarning("Could not get WCS information from header for chip %d, skipping", view->chip); 
+	  continue;
+	} 
+      }
+      
+      // determine RA,DEC of 4 corners, use to find RA_MIN,MAX, DEC_MIN,MAX
+      psRegion *region = pmChipPixels (chip);
+      psPlane ptCH[4], ptFP, ptTP;
+      psSphere ptSky;
+      ptCH[0].x = region->x0;
+      ptCH[0].y = region->y0;
+      ptCH[1].x = region->x1;
+      ptCH[1].y = region->y0;
+      ptCH[2].x = region->x1;
+      ptCH[2].y = region->y1;
+      ptCH[3].x = region->x0;
+      ptCH[3].y = region->y1;
+      psFree (region);
+      
+      for (int i = 0; i < 4; i++) {
+	psPlaneTransformApply (&ptFP, chip->toFPA, &ptCH[i]);
+	psPlaneTransformApply (&ptTP, fpa->toTPA, &ptFP);
+	psDeproject (&ptSky, &ptTP, fpa->toSky);
+
+	// rationalize ra to sky range centered on boresite
+	while (ptSky.r < RAminSky) ptSky.r += 2.0*M_PI;
+	while (ptSky.r > RAmaxSky) ptSky.r -= 2.0*M_PI;
+
+	RAmin = PS_MIN (ptSky.r, RAmin);
+	RAmax = PS_MAX (ptSky.r, RAmax);
+	
+	DECmin = PS_MIN (ptSky.d, DECmin);
+	DECmax = PS_MAX (ptSky.d, DECmax);
+
+	psLogMsg ("psastro", 2, "chip %d (corner %d) : %f %f  -> %f %f -> %f %f -> %f %f\n",
+		  view->chip, i, ptCH[i].x, ptCH[i].y, ptFP.x, ptFP.y, ptTP.x, ptTP.y, 
+		  DEG_RAD*ptSky.r, DEG_RAD*ptSky.d);
+      }
+    }
+    psLogMsg ("psastro", 3, "convert wcs terms to internal format : %f sec\n", psTimerMark ("psastro"));
+
+    psLogMsg ("psastro", 2, "loaded raw data from %f,%f to %f,%f\n",
+              DEG_RAD*RAmin, DEG_RAD*DECmin,
+              DEG_RAD*RAmax, DEG_RAD*DECmax);
+
+    psMetadataAddF32 (recipe, PS_LIST_TAIL, "RA_MIN",  PS_META_REPLACE, "", RAmin);
+    psMetadataAddF32 (recipe, PS_LIST_TAIL, "RA_MAX",  PS_META_REPLACE, "", RAmax);
+    psMetadataAddF32 (recipe, PS_LIST_TAIL, "DEC_MIN", PS_META_REPLACE, "", DECmin);
+    psMetadataAddF32 (recipe, PS_LIST_TAIL, "DEC_MAX", PS_META_REPLACE, "", DECmax);
+
+    psFree (view);
+    return true;
+}
Index: /branches/cnb_branches/cnb_branch_20090301/psastro/src/psastroExtractDataSave.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psastro/src/psastroExtractDataSave.c	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/psastro/src/psastroExtractDataSave.c	(revision 23594)
@@ -0,0 +1,52 @@
+/** @file psastroModelDataSave.c
+ *
+ *  @brief
+ *
+ *  @ingroup psastroModel
+ *
+ *  @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
+ */
+
+# include "psastroStandAlone.h"
+# define NONLIN_TOL 0.001 ///< tolerance in pixels 
+
+# define ESCAPE { \
+  psError(PS_ERR_UNKNOWN, false, "Failure in psastroModelDataSave"); \
+  psFree (view); \
+  return false; \
+}
+  
+bool psastroModelDataSave (pmConfig *config) {
+
+    bool status;
+
+    // select the current recipe
+    psMetadata *recipe  = psMetadataLookupPtr (&status, config->recipes, PSASTRO_RECIPE);
+    if (!recipe) {
+	psError(PSASTRO_ERR_CONFIG, true, "Can't find PSASTRO recipe");
+	return false;
+    }
+
+    pmFPAfile *output = psMetadataLookupPtr (&status, config->files, "PSASTRO.OUT.MODEL");
+    if (!status) psAbort ("Can't find output pmFPAfile PSASTRO.OUT.MODEL");
+
+    pmFPAview *view = pmFPAviewAlloc (0);
+    pmChip *chip = NULL;
+
+    while ((chip = pmFPAviewNextChip (view, output->fpa, 1)) != NULL) {
+        psTrace ("psastro", 4, "Chip %d: %x %x\n", view->chip, chip->file_exists, chip->process);
+        if (!chip->process) { continue; }
+	if (!pmFPAfileIOChecks (config, view, PM_FPA_AFTER)) ESCAPE;
+    }
+    if (!pmFPAfileIOChecks (config, view, PM_FPA_AFTER)) ESCAPE;
+
+    psLogMsg ("psastro", 3, "save headers : %f sec\n", psTimerMark ("psastro"));
+
+    return true;
+}
+
+
+
Index: /branches/cnb_branches/cnb_branch_20090301/psastro/src/psastroExtractFindChip.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psastro/src/psastroExtractFindChip.c	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/psastro/src/psastroExtractFindChip.c	(revision 23594)
@@ -0,0 +1,106 @@
+/** @file psastroExtractFindChip.c
+ *
+ *  @brief calculate chip position for the given FPA coords
+ *
+ *  @ingroup psastroExtract
+ *
+ *  @author IfA
+ *  @version $Revision: 1.7 $
+ *  @date $Date: 2009-02-07 02:03:34 $
+ *  Copyright 2009 Institute for Astronomy, University of Hawaii
+ */
+
+# include "psastroInternal.h"
+
+static psVector *chipXmin = NULL;
+static psVector *chipXmax = NULL;
+static psVector *chipYmin = NULL;
+static psVector *chipYmax = NULL;
+
+bool psastroExtractChipBounds (pmFPA *fpa) {
+
+    chipXmin = psVectorAlloc (fpa->chips->n, PS_TYPE_F32);
+    chipXmax = psVectorAlloc (fpa->chips->n, PS_TYPE_F32);
+    chipYmin = psVectorAlloc (fpa->chips->n, PS_TYPE_F32);
+    chipYmax = psVectorAlloc (fpa->chips->n, PS_TYPE_F32);
+
+    // this loop selects the matched stars for all chips
+    for (int i = 0; i < fpa->chips->n; i++) {
+
+      pmChip *chip = fpa->chips->data[i];
+      if (!chip->process || !chip->file_exists) { continue; }
+      if (!chip->fromFPA) { continue; }
+
+      // determine RA,DEC of 4 corners, use to find RA_MIN,MAX, DEC_MIN,MAX
+      psRegion *region = pmChipPixels (chip);
+      psPlane ptCH[4], ptFP;
+
+      ptCH[0].x = region->x0;
+      ptCH[0].y = region->y0;
+      ptCH[1].x = region->x1;
+      ptCH[1].y = region->y0;
+      ptCH[2].x = region->x1;
+      ptCH[2].y = region->y1;
+      ptCH[3].x = region->x0;
+      ptCH[3].y = region->y1;
+      psFree (region);
+      
+      double Xmin = +FLT_MAX;
+      double Xmax = -FLT_MAX;
+      double Ymin = +FLT_MAX;
+      double Ymax = -FLT_MAX;
+
+      for (int j = 0; j < 4; j++) {
+	psPlaneTransformApply (&ptFP, chip->toFPA, &ptCH[j]);
+	Xmin = PS_MIN (ptFP.x, Xmin);
+	Xmax = PS_MAX (ptFP.x, Xmax);
+	Ymin = PS_MIN (ptFP.y, Ymin);
+	Ymax = PS_MAX (ptFP.y, Ymax);
+      }
+
+      // fpa-range for the given chip
+      chipXmin->data.F32[i] = Xmin;
+      chipXmax->data.F32[i] = Xmax;
+      chipYmin->data.F32[i] = Ymin;
+      chipYmax->data.F32[i] = Ymax;
+    }
+
+    return true;
+}
+
+pmChip *psastroExtractFindChip (float *xChip, float *yChip, pmFPA *fpa, float xFPA, float yFPA) {
+
+    *xChip = NAN;
+    *yChip = NAN;
+
+    if (!chipXmin) {
+	psastroExtractChipBounds (fpa);
+    }
+
+    for (int i = 0; i < fpa->chips->n; i++) {
+
+	if (xFPA <  chipXmin->data.F32[i]) continue;
+	if (xFPA >= chipXmax->data.F32[i]) continue;
+	if (yFPA <  chipYmin->data.F32[i]) continue;
+	if (yFPA >= chipYmax->data.F32[i]) continue;
+
+	pmChip *chip = fpa->chips->data[i];
+	psRegion *region = pmChipPixels (chip);
+
+	psPlane ptCH, ptFP;
+	ptFP.x = xFPA;
+	ptFP.y = yFPA;
+	psPlaneTransformApply (&ptCH, chip->fromFPA, &ptFP);
+
+	if (ptCH.x <  region->x0) continue;
+	if (ptCH.x >= region->x1) continue;
+	if (ptCH.y <  region->y0) continue;
+	if (ptCH.y >= region->y1) continue;
+
+	*xChip = ptCH.x;
+	*yChip = ptCH.y;
+	return chip;
+    }
+
+    return NULL;
+}
Index: /branches/cnb_branches/cnb_branch_20090301/psastro/src/psastroExtractGhosts.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psastro/src/psastroExtractGhosts.c	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/psastro/src/psastroExtractGhosts.c	(revision 23594)
@@ -0,0 +1,131 @@
+/** @file psastroExtractGhosts.c
+ *
+ *  @brief calculate ghost FPA and Chip positions for the stars loaded on the FPA
+ *
+ *  @ingroup psastroExtract
+ *
+ *  @author IfA
+ *  @version $Revision: 1.7 $
+ *  @date $Date: 2009-02-07 02:03:34 $
+ *  Copyright 2009 Institute for Astronomy, University of Hawaii
+ */
+
+# include "psastroInternal.h"
+
+# define ESCAPE(MSG) {							\
+	psError(PS_ERR_UNKNOWN, false, "I/O failure in psastroMaskUpdate: %s", MSG); \
+	psFree (view);							\
+	return false;							\
+    }
+
+/**
+ * calculate ghost FPA and Chip positions for the stars loaded on the FPA
+ */
+bool psastroExtractGhosts (pmConfig *config) {
+
+    bool status;
+    pmChip *chip = NULL;
+    pmCell *cell = NULL;
+    pmReadout *readout = NULL;
+    float zeropt, exptime;
+
+    psLogMsg ("psastro", PS_LOG_INFO, "determine ghost positions");
+
+    // select the current recipe
+    psMetadata *recipe  = psMetadataLookupPtr (&status, config->recipes, PSASTRO_RECIPE);
+    if (!recipe) {
+        psError(PSASTRO_ERR_CONFIG, true, "Can't find PSASTRO recipe");
+        return false;
+    }
+
+    double EXTRACT_MAX_MAG = psMetadataLookupF32 (&status, recipe, "EXTRACT_MAX_MAG");
+
+    // we have two input pmFPAfiles: PSASTRO.EXTRACT.INPUT and PSASTRO.EXTRACT.ASTROM.  both are in chip-mosaic or fpa format
+    // we have already loaded the headers from PSASTRO.EXTRACT.ASTROM and determined the astrometry terms
+
+    // select the input astrometry data (also carries the refstars)
+    pmFPAfile *astrom = psMetadataLookupPtr (NULL, config->files, "PSASTRO.EXTRACT.ASTROM");
+    if (!astrom) {
+        psError(PSASTRO_ERR_CONFIG, true, "Can't find input data");
+        return false;
+    }
+    pmFPA *fpa = astrom->fpa;
+
+    // really error-out here?  or just skip?
+    if (!psastroZeroPointFromRecipe (&zeropt, &exptime, fpa, recipe)) {
+        psLogMsg ("psastro", PS_LOG_INFO, "failed to load zeropt data from recipe");
+        return false;
+    }
+
+    // recipe values are given in instrumental magnitudes
+    // use the zero point and exposure time to convert to apparent mags: M_ap = M_inst + C_0 + 2.5*log(exptime)
+    float MagOffset = zeropt + 2.5*log10(exptime);
+    EXTRACT_MAX_MAG += MagOffset;
+
+    pmFPAview *view = pmFPAviewAlloc (0);
+
+    // this loop selects the matched stars for all chips
+    while ((chip = pmFPAviewNextChip (view, fpa, 1)) != NULL) {
+        psTrace ("psastro", 4, "Chip %d: %x %x\n", view->chip, chip->file_exists, chip->process);
+        if (!chip->process || !chip->file_exists) { continue; }
+        if (!chip->fromFPA) { continue; }
+
+        while ((cell = pmFPAviewNextCell (view, 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; }
+
+            // process each of the readouts
+            while ((readout = pmFPAviewNextReadout (view, fpa, 1)) != NULL) {
+                if (! readout->data_exists) { continue; }
+
+                // select the raw objects for this readout (loaded in psastroExtract.c)
+                psArray *refstars = psMetadataLookupPtr (&status, readout->analysis, "PSASTRO.REFSTARS");
+                if (refstars == NULL) { continue; }
+
+                // identify the bright stars of interest
+                for (int i = 0; i < refstars->n; i++) {
+                    pmAstromObj *ref = refstars->data[i];
+                    if (ref->Mag > EXTRACT_MAX_MAG) continue;
+
+		    // XXX can make a more clever model...
+		    float xFPA = -1*ref->FP->x;
+		    float yFPA = -1*ref->FP->y;
+
+		    float xChip, yChip;
+		    pmChip *ghostChip = psastroExtractFindChip (&xChip, &yChip, fpa, xFPA, yFPA);
+		    if (!ghostChip) continue;
+		    if (!ghostChip->cells) continue;
+		    if (!ghostChip->cells->n) continue;
+		    pmCell *ghostCell = ghostChip->cells->data[0];
+		    if (!ghostCell) continue;
+		    if (!ghostCell->readouts) continue;
+		    if (!ghostCell->readouts->n) continue;
+		    pmReadout *ghostReadout = ghostCell->readouts->data[0];
+		    if (!ghostReadout) continue;
+
+		    psArray *ghosts = psMetadataLookupPtr (&status, ghostReadout->analysis, "PSASTRO.GHOSTS");
+		    if (ghosts == NULL) { 
+			ghosts = psArrayAllocEmpty (100);
+			psMetadataAdd (readout->analysis, PS_LIST_TAIL, "PSASTRO.GHOSTS", PS_DATA_ARRAY, "astrometry matches", ghosts);
+			psFree (ghosts);
+		    }
+
+		    pmAstromObj *ghost = pmAstromObjAlloc ();
+		    ghost->FP->x   = xFPA;
+		    ghost->FP->y   = yFPA;
+		    ghost->chip->x = xChip;
+		    ghost->chip->y = yChip;
+		    ghost->TP->x   = ref->FP->x; // XXX this is a bit of sleazy over-loading
+		    ghost->TP->y   = ref->FP->y;
+		    ghost->Mag     = ref->Mag;
+		    
+		    psArrayAdd (ghosts, 100, ghost);
+		    psFree (ghost);
+                }
+            }
+        }
+    }
+
+    psFree (view);
+    return true;
+}
Index: /branches/cnb_branches/cnb_branch_20090301/psastro/src/psastroExtractParseCamera.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psastro/src/psastroExtractParseCamera.c	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/psastro/src/psastroExtractParseCamera.c	(revision 23594)
@@ -0,0 +1,68 @@
+/** @file psastroExtractParseCamera.c
+ *
+ *  @brief
+ *
+ *  @ingroup psastroExtract
+ *
+ *  @author IfA
+ *  @version $Revision: 1.3 $ 
+ *  @date $Date: 2009-02-07 02:03:34 $
+ *  Copyright 2009 Institute for Astronomy, University of Hawaii
+ */
+
+# include "psastroInternal.h"
+
+bool psastroExtractParseCamera (pmConfig *config) {
+
+    bool status = false;
+
+    // the input image(s) are required arguments; they define the camera
+    status = false;
+    pmFPAfile *input = pmFPAfileDefineFromArgs(&status, config, "PSASTRO.EXTRACT.INPUT", "INPUT");
+    if (!input || !status) {
+        psError(PSASTRO_ERR_CONFIG, false, "Failed to build FPA from PSASTRO.EXTRACT.INPUT");
+        return false;
+    }
+
+    // the input image(s) are required arguments; they define the camera
+    status = false;
+    pmFPAfile *astrom = pmFPAfileDefineFromArgs(&status, config, "PSASTRO.EXTRACT.ASTROM", "ASTROM");
+    if (!astrom || !status) {
+        psError (PS_ERR_UNKNOWN, false, "failed to load astrometry definition");
+        return NULL;
+    }
+
+# if (0)    
+    // XXX for now, don't use the pmFPAfile methods
+    // set up an output fpa structure & file based on the last input file
+    pmFPAfile *output = pmFPAfileDefineOutput (config, input->fpa, "PSASTRO.OUT.MODEL");
+    if (!output) {
+	psError(PSASTRO_ERR_CONFIG, false, "Failed to build FPA for PSASTRO.OUT.MODEL from input");
+	return false;
+    }
+    output->save = true;
+# endif
+
+    // Chip selection: turn on only the chips specified (option is not required)
+    char *chipLine = psMetadataLookupStr(&status, config->arguments, "CHIP_SELECTIONS"); 
+    psArray *chips = psStringSplitArray (chipLine, ",", false);
+    if (chips->n > 0) {
+	pmFPASelectChip (input->fpa, -1, true); // deselect all chips
+	pmFPASelectChip (astrom->fpa, -1, true); // deselect all chips
+	for (int i = 0; i < chips->n; i++) {
+	    int chipNum = atoi(chips->data[i]);
+	    if (! pmFPASelectChip(input->fpa, chipNum, false)) {
+		psError(PSASTRO_ERR_CONFIG, true, "Chip number %d doesn't exist in camera.\n", chipNum);
+		return false;
+	    }
+	    if (! pmFPASelectChip(astrom->fpa, chipNum, false)) {
+		psError(PSASTRO_ERR_CONFIG, true, "Chip number %d doesn't exist in camera.\n", chipNum);
+		return false;
+	    }
+        }
+    }
+    psFree (chips);
+
+    psTrace("psastro", 1, "Done with psastroExtractParseCamera...\n");
+    return true;
+}
Index: /branches/cnb_branches/cnb_branch_20090301/psastro/src/psastroExtractStar.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psastro/src/psastroExtractStar.c	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/psastro/src/psastroExtractStar.c	(revision 23594)
@@ -0,0 +1,54 @@
+/** @file psastroExtractStar.c
+ *
+ *  @brief 
+ *
+ *  @ingroup psastroExtract
+ *
+ *  @author IfA
+ *  @version $Revision: 1.7 $
+ *  @date $Date: 2009-02-07 02:03:34 $
+ *  Copyright 2009 Institute for Astronomy, University of Hawaii
+ */
+
+# include "psastroInternal.h"
+
+psImage *psastroExtractStar (psImage *input, float x, float y, float dX, float dY) {
+
+    // skip if no pixels are on the image
+    // skip if center is not on the image
+    // if bounds fall off image, paste into a full-size image.
+
+    if (x < 0) return NULL;
+    if (y < 0) return NULL;
+    if (x >= input->numCols) return NULL;
+    if (y >= input->numRows) return NULL;
+
+    int xo = x; // index of center pixel (eg, 6.00 to 6.99 -> 6)
+    int yo = y;
+
+    // build an output image of size 2dX + 1, 2dY + 1
+    // psRegion fullRegion = psRegionSet (xo - dX, x + dX + 1, y - dY, y + dY + 1);
+    // psRegion realRegion = psRegionForImage (input, fullRegion);
+    // psImage *subset = psImageSubset (input, realRegion);
+
+    psImage *subset = psImageAlloc (2*dX+1, 2*dY+1, PS_TYPE_F32);
+    psImageInit (subset, 0.0);
+
+    // fill in the subset image with the values from the subset
+    // I must already have done this elsewhere...
+
+    for (int iy = yo - dY; iy < yo + dY + 1; iy++) {
+	for (int ix = xo - dX; ix < xo + dX + 1; ix++) {
+
+	    if (ix < input->col0) continue;
+	    if (iy < input->row0) continue;
+	    if (ix >= input->numCols) continue;
+	    if (iy >= input->numRows) continue;
+	
+	    int jx = ix + dX - xo; // runs from 0 to 2dX
+	    int jy = iy + dY - yo;
+	    subset->data.F32[jy][jx] = input->data.F32[iy][ix];
+	}
+    }
+    return subset;
+}
Index: /branches/cnb_branches/cnb_branch_20090301/psastro/src/psastroExtractStars.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psastro/src/psastroExtractStars.c	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/psastro/src/psastroExtractStars.c	(revision 23594)
@@ -0,0 +1,208 @@
+/** @file psastroExtractAnalysis.c
+ *
+ *  @brief 
+ *
+ *  @ingroup psastroExtract
+ *
+ *  @author IfA
+ *  @version $Revision: 1.7 $
+ *  @date $Date: 2009-02-07 02:03:34 $
+ *  Copyright 2009 Institute for Astronomy, University of Hawaii
+ */
+
+# include "psastroInternal.h"
+
+# define ESCAPE(MSG) {							\
+	psError(PS_ERR_UNKNOWN, false, "I/O failure in psastroMaskUpdate: %s", MSG); \
+	psFree (view);							\
+	return false;							\
+    }
+
+/**
+ * create a mask or mask regions based on the collection of reference stars that * are in the vicinity of each chip
+ */
+bool psastroExtractStars (pmConfig *config) {
+
+    bool status;
+    pmChip *chip = NULL;
+    pmCell *cell = NULL;
+    pmReadout *readout = NULL;
+    float zeropt, exptime;
+    char extname[81];
+
+    psLogMsg ("psastro", PS_LOG_INFO, "extracting bright-star subrasters");
+
+    // select the current recipe
+    psMetadata *recipe  = psMetadataLookupPtr (&status, config->recipes, PSASTRO_RECIPE);
+    if (!recipe) {
+        psError(PSASTRO_ERR_CONFIG, true, "Can't find PSASTRO recipe");
+        return false;
+    }
+
+    double EXTRACT_MAX_MAG = psMetadataLookupF32 (&status, recipe, "EXTRACT_MAX_MAG");
+
+    // we have two input pmFPAfiles: PSASTRO.EXTRACT.INPUT and PSASTRO.EXTRACT.ASTROM.  both are in chip-mosaic or fpa format
+    // we have already loaded the headers from PSASTRO.EXTRACT.ASTROM and determined the astrometry terms
+
+    // select the input astrometry data (also carries the refstars)
+    pmFPAfile *astrom = psMetadataLookupPtr (NULL, config->files, "PSASTRO.EXTRACT.ASTROM");
+    if (!astrom) {
+        psError(PSASTRO_ERR_CONFIG, true, "Can't find input data");
+        return false;
+    }
+    pmFPA *fpa = astrom->fpa;
+
+    // select the input data sources
+    pmFPAfile *input = psMetadataLookupPtr (NULL, config->files, "PSASTRO.EXTRACT.INPUT");
+    if (!input) {
+        psError(PSASTRO_ERR_CONFIG, true, "Can't find input data");
+        return false;
+    }
+
+    // only load data from INPUT
+    pmFPAfileActivate(config->files, false, NULL);
+    pmFPAfileActivate(config->files, true, "PSASTRO.EXTRACT.INPUT");
+
+    // really error-out here?  or just skip?
+    if (!psastroZeroPointFromRecipe (&zeropt, &exptime, fpa, recipe)) {
+        psLogMsg ("psastro", PS_LOG_INFO, "failed to load zeropt data from recipe");
+        return false;
+    }
+
+    // recipe values are given in instrumental magnitudes
+    // use the zero point and exposure time to convert to apparent mags: M_ap = M_inst + C_0 + 2.5*log(exptime)
+    float MagOffset = zeropt + 2.5*log10(exptime);
+    EXTRACT_MAX_MAG += MagOffset;
+
+    int nExt = 0;
+
+    // open the output file handle: we are just saving a series of extensions to this file
+    char *fileroot = psMetadataLookupStr (&status, config->arguments, "OUTPUT");
+    if (!fileroot) {
+	psLogMsg ("psastro", PS_LOG_INFO, "output fileroot not supplied");
+	return false;
+    }
+
+    char *filename = NULL;
+    psStringAppend (&filename, "%s.st.fits", fileroot);
+    psFits *outStars = psFitsOpen (filename, "w");
+    if (!outStars) {
+	psError(PS_ERR_IO, false, "error opening file %s\n", filename);
+	return false;
+    }
+    psFree (filename);
+
+    filename = NULL;
+    psStringAppend (&filename, "%s.gh.fits", fileroot);
+    psFits *outGhosts = psFitsOpen (filename, "w");
+    if (!outGhosts) {
+	psError(PS_ERR_IO, false, "error opening file %s\n", filename);
+	return false;
+    }
+    psFree (filename);
+
+    pmFPAview *view = pmFPAviewAlloc (0);
+
+    // generate a (very) basic header:
+    psMetadata *phu = psMetadataAlloc ();
+    // select the filter; default to fixed photcode and mag limit otherwise
+    char *filter = psMetadataLookupStr (&status, fpa->concepts, "FPA.FILTERID");
+    if (!status) ESCAPE("missing FPA.FILTER in concepts");
+
+    psMetadataAddStr (phu, PS_LIST_TAIL, "FILTER",  0, "filter", filter);
+    psMetadataAddF32 (phu, PS_LIST_TAIL, "EXPTIME", 0, "exptime", exptime);
+    psMetadataAddF32 (phu, PS_LIST_TAIL, "ZEROPT",  0, "zeropt", zeropt);
+
+    psFitsWriteBlank (outStars, phu, NULL);
+    psFitsWriteBlank (outGhosts, phu, NULL);
+    psFree (phu);
+
+    // open/load files as needed
+    if (!pmFPAfileIOChecks (config, view, PM_FPA_BEFORE)) ESCAPE("failed on FPA BEFORE");
+
+    // this loop selects the matched stars for all chips
+    while ((chip = pmFPAviewNextChip (view, fpa, 1)) != NULL) {
+        psTrace ("psastro", 4, "Chip %d: %x %x\n", view->chip, chip->file_exists, chip->process);
+        if (!chip->process || !chip->file_exists) { continue; }
+        if (!chip->fromFPA) { continue; }
+
+        if (!pmFPAfileIOChecks (config, view, PM_FPA_BEFORE)) ESCAPE("failed on Chip BEFORE");
+
+        while ((cell = pmFPAviewNextCell (view, 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; }
+
+            // process each of the readouts
+            while ((readout = pmFPAviewNextReadout (view, fpa, 1)) != NULL) {
+                if (! readout->data_exists) { continue; }
+
+                // select the raw objects for this readout (loaded in psastroExtract.c)
+                psArray *refstars = psMetadataLookupPtr (&status, readout->analysis, "PSASTRO.REFSTARS");
+                if (refstars == NULL) { continue; }
+
+                // identify the bright stars of interest
+                for (int i = 0; i < refstars->n; i++) {
+                    pmAstromObj *ref = refstars->data[i];
+                    if (ref->Mag > EXTRACT_MAX_MAG) continue;
+
+		    pmReadout *inReadout = pmFPAviewThisReadout (view, input->fpa);
+		    psImage *subraster = psastroExtractStar (inReadout->image, ref->chip->x, ref->chip->y, 200.0, 200.0);
+		    if (!subraster) continue;
+		    
+                    // generate a (very) basic header:
+                    psMetadata *header = psMetadataAlloc ();
+                    psMetadataAddF32 (header, PS_LIST_TAIL, "CHIP_X",  0, "chip coordinate",        ref->chip->x);
+                    psMetadataAddF32 (header, PS_LIST_TAIL, "CHIP_Y",  0, "chip coordinate",        ref->chip->y);
+                    psMetadataAddF32 (header, PS_LIST_TAIL, "FPA_X",   0, "focal-plane coordinate", ref->FP->x);
+                    psMetadataAddF32 (header, PS_LIST_TAIL, "FPA_Y",   0, "focal-plane coordinate", ref->FP->y);
+                    psMetadataAddF32 (header, PS_LIST_TAIL, "MAG",     0, "magnitude",              ref->Mag);
+
+		    snprintf (extname, 80, "extname.%05d", nExt);
+		    psFitsWriteImage (outStars, header, subraster, 0, extname);
+		    nExt ++;
+		    
+		    psFree (header);
+		    psFree (subraster);
+                }
+
+                // select the raw objects for this readout (loaded in psastroExtract.c)
+                psArray *ghosts = psMetadataLookupPtr (&status, readout->analysis, "PSASTRO.GHOSTS");
+                if (ghosts == NULL) { continue; }
+
+                // identify the bright stars of interest
+                for (int i = 0; i < ghosts->n; i++) {
+                    pmAstromObj *ghost = ghosts->data[i];
+
+		    pmReadout *inReadout = pmFPAviewThisReadout (view, input->fpa);
+		    psImage *subraster = psastroExtractStar (inReadout->image, ghost->chip->x, ghost->chip->y, 200.0, 200.0);
+		    if (!subraster) continue;
+		    
+                    // generate a (very) basic header:
+                    psMetadata *header = psMetadataAlloc ();
+                    psMetadataAddF32 (header, PS_LIST_TAIL, "CHIP_X",  0, "chip coordinate",        ghost->chip->x);
+                    psMetadataAddF32 (header, PS_LIST_TAIL, "CHIP_Y",  0, "chip coordinate",        ghost->chip->y);
+                    psMetadataAddF32 (header, PS_LIST_TAIL, "FPA_X",   0, "focal-plane coordinate", ghost->FP->x);
+                    psMetadataAddF32 (header, PS_LIST_TAIL, "FPA_Y",   0, "focal-plane coordinate", ghost->FP->y);
+                    psMetadataAddF32 (header, PS_LIST_TAIL, "FPA_X0",  0, "star focal-plane coordinate", ghost->TP->x); // note : over-loaded value 
+                    psMetadataAddF32 (header, PS_LIST_TAIL, "FPA_Y0",  0, "star focal-plane coordinate", ghost->TP->y); // note : over-loaded value 
+                    psMetadataAddF32 (header, PS_LIST_TAIL, "MAG",     0, "magnitude",              ghost->Mag);
+
+		    snprintf (extname, 80, "extname.%05d", nExt);
+		    psFitsWriteImage (outStars, header, subraster, 0, extname);
+		    nExt ++;
+		    
+		    psFree (header);
+		    psFree (subraster);
+                }
+                if (!pmFPAfileIOChecks (config, view, PM_FPA_AFTER)) ESCAPE("failed on Readout AFTER");;
+            }
+            if (!pmFPAfileIOChecks (config, view, PM_FPA_AFTER)) ESCAPE("failed on Cell AFTER");;
+        }
+        if (!pmFPAfileIOChecks (config, view, PM_FPA_AFTER)) ESCAPE("failed on Chip AFTER");;
+    }
+    if (!pmFPAfileIOChecks (config, view, PM_FPA_AFTER)) ESCAPE("failed on FPA AFTER");;
+
+    psFitsClose (outStars);
+    psFree (view);
+    return true;
+}
Index: /branches/cnb_branches/cnb_branch_20090301/psastro/src/psastroLoadRefstars.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psastro/src/psastroLoadRefstars.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/psastro/src/psastroLoadRefstars.c	(revision 23594)
@@ -14,7 +14,5 @@
 # define ELIXIR_MODE 1
 
-char *psastroSetMagLimit (float *minMag, float *maxRho, pmConfig *config);
-
-psArray *psastroLoadRefstars (pmConfig *config) {
+psArray *psastroLoadRefstars (pmConfig *config, const char *source) {
 
     int fd;
@@ -69,5 +67,5 @@
     float minMag;
     float maxRho;
-    char *photcode = psastroSetMagLimit (&minMag, &maxRho, config);
+    char *photcode = psastroSetMagLimit (&minMag, &maxRho, config, source);
     PS_ASSERT (photcode, NULL);
 
@@ -240,5 +238,5 @@
   goto escape; }
 
-char *psastroSetMagLimit (float *minMag, float *maxRho, pmConfig *config) {
+char *psastroSetMagLimit (float *minMag, float *maxRho, pmConfig *config, const char *source) {
 
     bool status;
@@ -249,5 +247,5 @@
 
     // select the input data sources
-    pmFPAfile *input = psMetadataLookupPtr (NULL, config->files, "PSASTRO.INPUT");
+    pmFPAfile *input = psMetadataLookupPtr (NULL, config->files, source);
     if (!input) {
         psLogMsg ("psastro", PS_LOG_DETAIL, "no supplied reference header data");
Index: /branches/cnb_branches/cnb_branch_20090301/psconfig/psbuild
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psconfig/psbuild	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/psconfig/psbuild	(revision 23594)
@@ -14,4 +14,5 @@
 $verbose = 0;
 $use_svn = 1;
+$svn_trunk = "";
 
 $extlibs = "none";
@@ -34,4 +35,8 @@
     if ($ARGV[0] eq "-extperl") {
         $extperl = $ARGV[1];
+        shift; shift; next;
+    }
+    if ($ARGV[0] eq "-trunk") {
+        $svn_trunk = $ARGV[1];
         shift; shift; next;
     }
@@ -293,4 +298,10 @@
         print "\033]0; ** psbuild: $module[$i] ** \007";
 
+        if ($svn_trunk && (!-d $workdir || !-r $workdir || !-x $workdir)) {
+	    # try trunk instead
+            print STDERR "$module[$i] missing from local path, trying trunk path\n";
+	    $workdir = "$svn_trunk/$module[$i]";
+        }
+
         if (!-d $workdir || !-r $workdir || !-x $workdir) {
             print STDERR "WARNING: no directory for component $module[$i], skipping\n";
Index: /branches/cnb_branches/cnb_branch_20090301/psconfig/psconfig.csh.in
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psconfig/psconfig.csh.in	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/psconfig/psconfig.csh.in	(revision 23594)
@@ -410,6 +410,6 @@
 
 if ("$PSCONFIG" == "none") then
-  alias  psconfigure configure
-  alias  psautogen autogen.sh
+  alias  psconfigure ./configure
+  alias  psautogen ./autogen.sh
   alias  psperlbuild perl Build.PL
 else
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 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/psconfig/tagsets/ipp-2.8.dist	(revision 23594)
@@ -33,4 +33,5 @@
   YNNYY  Nebulous/nebclient     ipp-2-8          -0
   YNNYY  Nebulous               ipp-2-8          -0
+  YNNYY  Nebulous-Server        ipp-2-8          -0
   YYYYY  PS-IPP-Metadata-Config ipp-2-8          -0
   YYYYY  PS-IPP-Config          ipp-2-8          -0     
@@ -68,3 +69,5 @@
   YYYYY  DataStore              ipp-2-8          -0
 
+  YNNNN  extsrc/gpcsw           ipp-2-8          -0
+
 # there are externally required C libraries and perl modules (see INSTALL)
Index: /branches/cnb_branches/cnb_branch_20090301/psphot/src/psphot.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psphot/src/psphot.h	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/psphot/src/psphot.h	(revision 23594)
@@ -23,5 +23,5 @@
 
 bool            psphotModelTest (pmConfig *config, const pmFPAview *view, psMetadata *recipe);
-bool            psphotInit ();
+bool            psphotInit (void);
 bool            psphotReadout (pmConfig *config, const pmFPAview *view);
 bool            psphotReadoutFindPSF(pmConfig *config, const pmFPAview *view, psArray *inSources);
@@ -35,5 +35,5 @@
 
 // XXX test functions
-psArray        *psphotFakeSources ();
+psArray        *psphotFakeSources (void);
 
 // psphotReadout functions
@@ -76,5 +76,5 @@
 
 // thread-related:
-bool            psphotSetThreads ();
+bool            psphotSetThreads (void);
 bool            psphotChooseCellSizes (int *Cx, int *Cy, pmReadout *readout, int nThreads);
 bool            psphotCoordToCell (int *group, int *cell, float x, float y, int Cx, int Cy);
@@ -136,6 +136,6 @@
 bool            psphotPlotMoments (pmConfig *config, pmFPAview *view, psArray *sources);
 bool            psphotPlotPSFModel (pmConfig *config, pmFPAview *view, psArray *sources);
-bool            psphotFitInit ();
-bool            psphotFitSummary ();
+bool            psphotFitInit (int nThreads);
+bool            psphotFitSummary (void);
 
 bool            psphotMergeSources (psArray *oldSources, psArray *newSources);
@@ -230,6 +230,6 @@
 psImage *pmPCMDataSaveImage (pmPCMData *pcm);
 
-int psphotKapaOpen ();
-bool psphotKapaClose ();
+int psphotKapaOpen (void);
+bool psphotKapaClose (void);
 bool psphotImageBackgroundCellHistogram (psVector *values, float mean, float sigma, int ix, int iy);
 bool psphotDiagnosticPlots (const pmConfig *config, const char *name, ...);
Index: /branches/cnb_branches/cnb_branch_20090301/psphot/src/psphotCleanup.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psphot/src/psphotCleanup.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/psphot/src/psphotCleanup.c	(revision 23594)
@@ -18,10 +18,10 @@
 }
 
-psExit psphotGetExitStatus () {
+psExit psphotGetExitStatus (void) {
 
     psErrorCode err = psErrorCodeLast ();
     switch (err) {
       case PS_ERR_NONE:
-	return PS_EXIT_SUCCESS;
+        return PS_EXIT_SUCCESS;
       case PSPHOT_ERR_SYS:
         return PS_EXIT_SYS_ERROR;
Index: /branches/cnb_branches/cnb_branch_20090301/psphot/src/psphotDetect.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psphot/src/psphotDetect.h	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/psphot/src/psphotDetect.h	(revision 23594)
@@ -19,5 +19,5 @@
 bool        psphotMosaicChip(pmConfig *config, const pmFPAview *view, char *outFile, char *inFile);
 void        psphotCleanup (pmConfig *config);
-psExit      psphotGetExitStatus ();
+psExit      psphotGetExitStatus (void);
 
 #endif
Index: /branches/cnb_branches/cnb_branch_20090301/psphot/src/psphotFitSourcesLinear.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psphot/src/psphotFitSourcesLinear.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/psphot/src/psphotFitSourcesLinear.c	(revision 23594)
@@ -63,6 +63,6 @@
         pmSource *source = sources->data[i];
 
-	// turn this bit off and turn it on again if we pass this test
-	source->mode &= ~PM_SOURCE_MODE_LINEAR_FIT;
+        // turn this bit off and turn it on again if we pass this test
+        source->mode &= ~PM_SOURCE_MODE_LINEAR_FIT;
 
         // skip non-astronomical objects (very likely defects)
@@ -70,5 +70,5 @@
         if (source->type == PM_SOURCE_TYPE_SATURATED) continue;
 
-	// do not include CRs in the full ensemble fit
+        // do not include CRs in the full ensemble fit
         if (source->mode & PM_SOURCE_MODE_CR_LIMIT) continue;
 
@@ -94,8 +94,12 @@
         if (y > AnalysisRegion.y1) continue;
 
-	source->mode |= PM_SOURCE_MODE_LINEAR_FIT;
+        source->mode |= PM_SOURCE_MODE_LINEAR_FIT;
         psArrayAdd (fitSources, 100, source);
     }
     psLogMsg ("psphot.ensemble", PS_LOG_MINUTIA, "built fitSources: %f sec (%ld objects)\n", psTimerMark ("psphot.linear"), sources->n);
+
+    if (fitSources->n == 0) {
+        return true;
+    }
 
     // vectors to store stats for each object
@@ -189,8 +193,8 @@
     psLogMsg ("psphot.ensemble", PS_LOG_MINUTIA, "solve matrix: %f sec (%d elements)\n", psTimerMark ("psphot.linear"), sparse->Nelem);
 
-    // XXXX **** philosophical question: 
+    // XXXX **** philosophical question:
     // we measure bright objects in three passes: 1) linear fit; 2) non-linear fit; 3) linear fit:
-    // should retain the chisq and errors from the intermediate non-linear fit? 
-    // the non-linear fit provides better values for the position errors, and for 
+    // should retain the chisq and errors from the intermediate non-linear fit?
+    // the non-linear fit provides better values for the position errors, and for
     // extended sources, the shape errors
 
@@ -218,5 +222,5 @@
     for (int i = 0; final && (i < fitSources->n); i++) {
         pmSource *source = fitSources->data[i];
-	if (source->mode & PM_SOURCE_MODE_NONLINEAR_FIT) continue;
+        if (source->mode & PM_SOURCE_MODE_NONLINEAR_FIT) continue;
         pmModel *model = pmSourceGetModel (NULL, source);
         pmSourceChisq (model, source->pixels, source->maskObj, source->variance, maskVal);
Index: /branches/cnb_branches/cnb_branch_20090301/psphot/src/psphotMaskReadout.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psphot/src/psphotMaskReadout.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/psphot/src/psphotMaskReadout.c	(revision 23594)
@@ -12,5 +12,9 @@
     psMetadataAddImageMask (recipe, PS_LIST_TAIL, "MASK.SAT", PS_META_REPLACE, "user-defined mask", maskSat);
 
-    psImageMaskType maskBad  = pmConfigMaskGet("BAD", config); // Mask value for bad pixels
+    psImageMaskType maskBad  = pmConfigMaskGet("LOW", config); // Mask value for low pixels
+    if (!maskBad) {
+        // XXX: for backward compatability look up old name
+        maskBad  = pmConfigMaskGet("BAD", config);
+    }
     psMetadataAddImageMask (recipe, PS_LIST_TAIL, "MASK.BAD", PS_META_REPLACE, "user-defined mask", maskBad);
 
Index: /branches/cnb_branches/cnb_branch_20090301/psphot/src/psphotReadout.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psphot/src/psphotReadout.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/psphot/src/psphotReadout.c	(revision 23594)
@@ -2,5 +2,5 @@
 
 // this should be called by every program that links against libpsphot
-bool psphotInit () {
+bool psphotInit (void) {
 
     psphotErrorRegister();              // register our error codes/messages
Index: /branches/cnb_branches/cnb_branch_20090301/psphot/src/psphotReadoutFindPSF.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psphot/src/psphotReadoutFindPSF.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/psphot/src/psphotReadoutFindPSF.c	(revision 23594)
@@ -11,4 +11,10 @@
     if (!recipe) {
         psError(PSPHOT_ERR_CONFIG, false, "missing recipe %s", PSPHOT_RECIPE);
+        return false;
+    }
+
+    // set the photcode for this image
+    if (!psphotAddPhotcode(recipe, config, view, "PSPHOT.INPUT")) {
+        psError(PSPHOT_ERR_CONFIG, false, "trouble defining the photcode");
         return false;
     }
Index: /branches/cnb_branches/cnb_branch_20090301/psphot/src/psphotReadoutKnownSources.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psphot/src/psphotReadoutKnownSources.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/psphot/src/psphotReadoutKnownSources.c	(revision 23594)
@@ -11,4 +11,10 @@
     if (!recipe) {
         psError(PSPHOT_ERR_CONFIG, false, "missing recipe %s", PSPHOT_RECIPE);
+        return false;
+    }
+
+    // set the photcode for this image
+    if (!psphotAddPhotcode(recipe, config, view, "PSPHOT.INPUT")) {
+        psError(PSPHOT_ERR_CONFIG, false, "trouble defining the photcode");
         return false;
     }
Index: /branches/cnb_branches/cnb_branch_20090301/psphot/src/psphotReadoutMinimal.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psphot/src/psphotReadoutMinimal.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/psphot/src/psphotReadoutMinimal.c	(revision 23594)
@@ -19,4 +19,10 @@
     if (!recipe) {
         psError(PSPHOT_ERR_CONFIG, false, "missing recipe %s", PSPHOT_RECIPE);
+        return false;
+    }
+
+    // set the photcode for this image
+    if (!psphotAddPhotcode(recipe, config, view, "PSPHOT.INPUT")) {
+        psError(PSPHOT_ERR_CONFIG, false, "trouble defining the photcode");
         return false;
     }
Index: /branches/cnb_branches/cnb_branch_20090301/psphot/src/psphotStandAlone.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psphot/src/psphotStandAlone.h	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/psphot/src/psphotStandAlone.h	(revision 23594)
@@ -17,5 +17,5 @@
 bool            psphotMosaicChip(pmConfig *config, const pmFPAview *view, char *outFile, char *inFile);
 void            psphotCleanup (pmConfig *config);
-psExit          psphotGetExitStatus ();
+psExit          psphotGetExitStatus (void);
 
 #endif
Index: /branches/cnb_branches/cnb_branch_20090301/pswarp/src/pswarp.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/pswarp/src/pswarp.h	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/pswarp/src/pswarp.h	(revision 23594)
@@ -72,5 +72,5 @@
 } pswarpTransformTileArgs;
 
-pswarpTransformTileArgs *pswarpTransformTileArgsAlloc();
+pswarpTransformTileArgs *pswarpTransformTileArgsAlloc(void);
 bool pswarpTransformTile (pswarpTransformTileArgs *args);
 
@@ -87,5 +87,5 @@
 bool pswarpMatchRange (int *minX, int *minY, int *maxX, int *maxY, pmReadout *dest, pmReadout *src);
 
-pswarpMap *pswarpMapAlloc ();
+pswarpMap *pswarpMapAlloc (void);
 pswarpMapGrid *pswarpMapGridAlloc (int Nx, int Ny);
 
@@ -111,5 +111,5 @@
  * define threads for this program
  */
-bool pswarpSetThreads ();
+bool pswarpSetThreads (void);
 
 /// Return software version
Index: /branches/cnb_branches/cnb_branch_20090301/pswarp/src/pswarpMapGrid.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/pswarp/src/pswarpMapGrid.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/pswarp/src/pswarpMapGrid.c	(revision 23594)
@@ -43,9 +43,9 @@
     // measure the map for the center of each superpixel
     for (ni = 0, i = xMin; ni < nXpts; i += nXpix, ni++) {
-	for (nj = 0, j = yMin; nj < nYpts; j += nYpix, nj++) {
-	    pswarpMapSetLocalModel (grid->maps[ni][nj], dest, src, i, j);
-	}
+        for (nj = 0, j = yMin; nj < nYpts; j += nYpix, nj++) {
+            pswarpMapSetLocalModel (grid->maps[ni][nj], dest, src, i, j);
+        }
     }
-	    
+
     grid->nXpix = nXpix;
     grid->nYpix = nYpix;
@@ -112,15 +112,15 @@
 
     for (int i = 0; i < grid->nXpts - 1; i++) {
-	for (int j = 0; j < grid->nYpts - 1; j++) {
-
-	    // measure the output coordinates for the next grid position using the current grid map
-	    // compare with the coordinates measured using the next grid map
-	    pswarpMapApply (&xRaw, &yRaw, grid->maps[i][j], grid->maps[i][j]->xo + grid->nXpix, grid->maps[i][j]->yo);
-	    pswarpMapApply (&xRef, &yRef, grid->maps[i+1][j], grid->maps[i][j]->xo + grid->nXpix, grid->maps[i][j]->yo);
-
-	    double posError = hypot (xRaw-xRef, yRaw-yRef);
-	    maxError = PS_MAX (maxError, posError);
-	}
-    }	
+        for (int j = 0; j < grid->nYpts - 1; j++) {
+
+            // measure the output coordinates for the next grid position using the current grid map
+            // compare with the coordinates measured using the next grid map
+            pswarpMapApply (&xRaw, &yRaw, grid->maps[i][j], grid->maps[i][j]->xo + grid->nXpix, grid->maps[i][j]->yo);
+            pswarpMapApply (&xRef, &yRef, grid->maps[i+1][j], grid->maps[i][j]->xo + grid->nXpix, grid->maps[i][j]->yo);
+
+            double posError = hypot (xRaw-xRef, yRaw-yRef);
+            maxError = PS_MAX (maxError, posError);
+        }
+    }
     return maxError;
 }
@@ -176,5 +176,5 @@
     psPlaneTransformApply (FP, fpaDest->fromTPA, TP);
     psPlaneTransformApply (V00, chipDest->fromFPA, FP);
-    
+
     /** V(1,0) position */
     offset->x = ix + 1;
@@ -186,5 +186,5 @@
     psPlaneTransformApply (FP, fpaDest->fromTPA, TP);
     psPlaneTransformApply (V10, chipDest->fromFPA, FP);
-    
+
     /** V(0,1) position */
     offset->x = ix;
@@ -204,5 +204,5 @@
     map->Yy = V01->y - V00->y;
     map->Yo = V00->y - map->Yx*ix - map->Yy*iy;
- 
+
     map->xo = ix;
     map->yo = iy;
@@ -224,5 +224,5 @@
 }
 
-pswarpMap *pswarpMapAlloc () {
+pswarpMap *pswarpMapAlloc(void) {
 
   pswarpMap *map = (pswarpMap *) psAlloc (sizeof(pswarpMap));
@@ -238,8 +238,8 @@
 
     for (int i = 0; i < grid->nXpts; i++) {
-	for (int j = 0; j < grid->nYpts; j++) {
-	    psFree (grid->maps[i][j]);
-	}
-	psFree (grid->maps[i]);
+        for (int j = 0; j < grid->nYpts; j++) {
+            psFree (grid->maps[i][j]);
+        }
+        psFree (grid->maps[i]);
     }
     psFree (grid->maps);
@@ -256,5 +256,5 @@
       grid->maps[i] = psAlloc (nYpts*sizeof(void *));
       for (int j = 0; j < nYpts; j++) {
-	  grid->maps[i][j] = pswarpMapAlloc();
+          grid->maps[i][j] = pswarpMapAlloc();
       }
   }
@@ -264,5 +264,5 @@
   grid->nXpix = 0;
   grid->nYpix = 0;
-  
+
   return grid;
 }
Index: /branches/cnb_branches/cnb_branch_20090301/pswarp/src/pswarpParseCamera.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/pswarp/src/pswarpParseCamera.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/pswarp/src/pswarpParseCamera.c	(revision 23594)
@@ -24,5 +24,5 @@
 
     // look for the file on the RUN metadata
-    pmFPAfile *file = pmFPAfileDefineFromRun(&status, config, filerule); // File to return
+    pmFPAfile *file = pmFPAfileDefineFromRun(&status, bind, config, filerule); // File to return
     if (!status) {
         psError(PSWARP_ERR_CONFIG, false, "Failed to load file definition for %s", filerule);
Index: /branches/cnb_branches/cnb_branch_20090301/pswarp/src/pswarpSetMaskBits.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/pswarp/src/pswarpSetMaskBits.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/pswarp/src/pswarpSetMaskBits.c	(revision 23594)
@@ -33,16 +33,16 @@
 
     // mask for non-linear flat regions (default to DETECTOR if not defined)
-    psImageMaskType badMask = pmConfigMaskGet("BAD.WARP", config);
+    psImageMaskType badMask = pmConfigMaskGet("CONV.BAD", config);
     if (!badMask) {
         badMask = 0x01;
-        pmConfigMaskSet (config, "BAD.WARP", badMask);
+        pmConfigMaskSet (config, "CONV.BAD", badMask);
     }
     maskOut |= badMask;
 
     // mask for non-linear flat regions (default to DETECTOR if not defined)
-    psImageMaskType poorMask = pmConfigMaskGet("POOR.WARP", config);
+    psImageMaskType poorMask = pmConfigMaskGet("CONV.POOR", config);
     if (!poorMask) {
         poorMask = 0x02;
-        pmConfigMaskSet (config, "POOR.WARP", poorMask);
+        pmConfigMaskSet (config, "CONV.POOR", poorMask);
     }
     maskOut |= poorMask;
Index: /branches/cnb_branches/cnb_branch_20090301/pswarp/src/pswarpSetThreads.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/pswarp/src/pswarpSetThreads.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/pswarp/src/pswarpSetThreads.c	(revision 23594)
@@ -23,5 +23,5 @@
 }
 
-bool pswarpSetThreads () {
+bool pswarpSetThreads(void) {
 
     psThreadTask *task = NULL;
Index: /branches/cnb_branches/cnb_branch_20090301/pswarp/src/pswarpTransformReadout.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/pswarp/src/pswarpTransformReadout.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/pswarp/src/pswarpTransformReadout.c	(revision 23594)
@@ -37,6 +37,12 @@
     // output mask bits
     psImageMaskType maskIn   = psMetadataLookupImageMask(&mdok, recipe, "MASK.INPUT");
-    psImageMaskType maskPoor = pmConfigMaskGet("POOR.WARP", config);
-    psImageMaskType maskBad  = pmConfigMaskGet("BAD.WARP", config);
+    psImageMaskType maskPoor = pmConfigMaskGet("CONV.POOR", config);
+    if (!maskPoor) {
+        maskPoor = pmConfigMaskGet("POOR.WARP", config);
+    }
+    psImageMaskType maskBad  = pmConfigMaskGet("CONV.BAD", config);
+    if (!maskBad) {
+        maskBad  = pmConfigMaskGet("BAD.WARP", config);
+    }
     psAssert(mdok, "MASK.INPUT was not defined");
 
Index: /branches/cnb_branches/cnb_branch_20090301/pswarp/src/pswarpTransformTile.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/pswarp/src/pswarpTransformTile.c	(revision 23593)
+++ /branches/cnb_branches/cnb_branch_20090301/pswarp/src/pswarpTransformTile.c	(revision 23594)
@@ -24,5 +24,5 @@
 }
 
-pswarpTransformTileArgs *pswarpTransformTileArgsAlloc()
+pswarpTransformTileArgs *pswarpTransformTileArgsAlloc(void)
 {
     pswarpTransformTileArgs *args = psAlloc(sizeof(pswarpTransformTileArgs));
Index: /branches/cnb_branches/cnb_branch_20090301/tools/ipp_apply_burntool.pl
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/tools/ipp_apply_burntool.pl	(revision 23594)
+++ /branches/cnb_branches/cnb_branch_20090301/tools/ipp_apply_burntool.pl	(revision 23594)
@@ -0,0 +1,176 @@
+#!/usr/bin/env perl
+# this program is used to run 'burntool' on images for a single chip
+# for all exposures for a given time period.   
+# USAGE: ipp_apply_burntool.pl --dbname gpc1 --class_id XY00 --dateobs_begin YYYY/MM/DD --dateobs_end YYYY/MM/DD 
+
+# XXX todo:
+# - add filters to processedimfile
+# 
+
+use warnings;
+use strict;
+use Carp;
+
+use IPC::Cmd 0.36 qw( can_run );
+use IPC::Run 0.36 qw( run );
+use PS::IPP::Metadata::List qw( parse_md_list );
+use PS::IPP::Config 1.01 qw( :standard );
+use File::Temp qw( tempfile );
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+
+my ( $class_id, $dateobs_begin, $dateobs_end, $skip_burned, $dbname, $verbose, $save_temps);
+GetOptions(
+    'class_id=s'        => \$class_id, # chip identifier
+    'dateobs_begin=s'   => \$dateobs_begin, # exposure date/time range start
+    'dateobs_end=s'     => \$dateobs_end, # exposure date/time range stop
+    'dbname|d=s'        => \$dbname, # Database name
+    'skip_burned'       => \$skip_burned,   # Print to stdout
+    'verbose'           => \$verbose,   # Print to stdout
+    'save-temps'        => \$save_temps, # Save temporary files?
+    ) or pod2usage( 2 );
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage(
+          -msg => "Required options: --class_id --dateobs_begin --dateobs_end --dbname",
+          -exitval => 3,
+          ) unless
+    defined $class_id and
+    defined $dateobs_begin and
+    defined $dateobs_end and
+    defined $dbname;
+
+my $missing_tools;
+my $regtool  = can_run('regtool')  or (warn "Can't find regtool" and $missing_tools = 1);
+my $funpack  = can_run('funpack')  or (warn "Can't find funpack" and $missing_tools = 1);
+my $burntool = can_run('burntool') or (warn "Can't find burntool" and $missing_tools = 1);
+my $fpack    = can_run('fpack')    or (warn "Can't find fpack" and $missing_tools = 1);
+if ($missing_tools) {
+    warn("Can't find required tools.");
+    exit($PS_EXIT_CONFIG_ERROR);
+}
+
+my $mdcParser = PS::IPP::Metadata::Config->new; # Parser for metadata config files
+
+my $command = "$regtool -processedimfile";
+$command .= " -class_id $class_id";
+$command .= " -dateobs_begin $dateobs_begin";
+$command .= " -dateobs_end $dateobs_end";
+# $command .= " -limit 20";
+$command .= " -dbname $dbname" if defined $dbname;
+
+my @command = split /\s+/, $command;
+my ( $stdin, $stdout, $stderr ); # Buffers for running program
+print "Running [$command]...\n" if $verbose;
+if (not run(\@command, \$stdin, \$stdout, \$stderr)) {
+    &my_die("Unable to perform regtool -processedimfile");
+}
+print $stdout . "\n" if $verbose;
+
+my @files;
+my @whole = split /\n/, $stdout;
+my @single = ();
+while ( scalar @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 ) ) );
+	&my_die("Unable to parse output from regtool") unless defined $list;
+	push @files, @$list;
+	@single = ();
+    }
+}
+
+# IPP configuration (including nebulous)
+my $ipprc = PS::IPP::Config->new() or my_die("Unable to set up");
+
+my $Nfiles = @files;
+print "files: $Nfiles\n";
+
+my $REALRUN = 1;
+
+my $prevFileOpt = "";
+foreach my $file (@files) {
+    my $exp_id = $file->{exp_id};
+    if ($skip_burned and ($file->{user_1} > 0.5)) { next; }
+
+    my $rawImfile = $file->{uri};
+    my $rawImfileReal;
+    if ($REALRUN) {
+	$rawImfileReal = $ipprc->file_resolve($rawImfile, 0);
+    } else {
+	($rawImfileReal) = $rawImfile =~ m|^neb:/(.*)|;
+    }
+    # print "rawImfile: $rawImfile -> $rawImfileReal\n";
+
+    # mangle name, create tmp file (always a UNIX file)
+    my $basename = `basename $rawImfile`; chomp $basename;
+    my $tempfile = new File::Temp ( TEMPLATE => "$basename.XXXX", 
+				    DIR => '/tmp',
+				    UNLINK => !$save_temps);
+    my $tmpImfileReal = $tempfile->filename;
+    # print "tmpImfile: $tmpImfile -> $tmpImfileReal\n";
+    
+    # destination for the burntool-applied image file (may be a NEB file)
+    my $outImfile = $rawImfile;
+    $outImfile =~ s/fits$/burn.fits/;
+    my $outImfileReal = $ipprc->file_resolve($outImfile, 1);
+    # print "outImfile: $outImfile -> $outImfileReal\n";
+
+    # destination for the burntool artifacts
+    my $artImfile = $rawImfile;
+    $artImfile =~ s/fits$/burn.tbl/;
+    my $artImfileReal = $ipprc->file_resolve($artImfile, 1);
+    # print "artImfile: $artImfile -> $artImfileReal\n";
+
+    # uncompress the image (do we need to check if it is compressed?)
+    my $status = vsystem ("$funpack -S $rawImfileReal > $tmpImfileReal", $REALRUN);
+    if ($status) {
+	&my_die("failed on funpack");
+    }
+
+    $status = vsystem ("$burntool $tmpImfileReal out=$artImfileReal $prevFileOpt", $REALRUN);
+    if ($status) {
+	&my_die("failed on burntool");
+    }
+
+    # compress the image (do we need to check if it is compressed?)
+    $status = vsystem ("$fpack -S $tmpImfileReal > $outImfileReal", $REALRUN);
+    if ($status) {
+	&my_die("failed on fpack");
+    }
+
+    $status = vsystem ("$regtool -dbname $dbname -updateprocessedimfile -exp_id $exp_id -class_id $class_id -user_1 1.0", 1);
+    if ($status) {
+	&my_die("failed to update imfile");
+    }
+
+    # save the artifact file for the next image
+    $prevFileOpt = "in=$artImfileReal";
+    print "\n";
+}
+
+exit 0;
+
+sub vsystem {
+    my $command = shift;
+    my $realrun = shift;
+
+    print "$command\n";
+    
+    my $status = 0;
+    if ($realrun) {
+	$status = system ($command);
+    }
+
+    return $status;
+}
+
+sub my_die {
+    my $message = shift;
+
+    printf STDERR "$message\n";
+    exit 1;
+}
